From ee92c98f34d5cedc996ed6b956396915e4e6f77e Mon Sep 17 00:00:00 2001 From: abutalib-kiwi Date: Tue, 1 Aug 2023 13:12:37 +0530 Subject: [PATCH 01/61] article list api changes, changed related name for survey_options to options --- .../0004_alter_surveyoption_survey.py | 19 +++++++++++++++++++ web_admin/models.py | 2 +- web_admin/views/article.py | 2 +- 3 files changed, 21 insertions(+), 2 deletions(-) create mode 100644 web_admin/migrations/0004_alter_surveyoption_survey.py diff --git a/web_admin/migrations/0004_alter_surveyoption_survey.py b/web_admin/migrations/0004_alter_surveyoption_survey.py new file mode 100644 index 0000000..8d28957 --- /dev/null +++ b/web_admin/migrations/0004_alter_surveyoption_survey.py @@ -0,0 +1,19 @@ +# Generated by Django 4.2.2 on 2023-08-01 07:35 + +from django.db import migrations, models +import django.db.models.deletion + + +class Migration(migrations.Migration): + + dependencies = [ + ('web_admin', '0003_defaultarticlecardimage_and_more'), + ] + + operations = [ + migrations.AlterField( + model_name='surveyoption', + name='survey', + field=models.ForeignKey(on_delete=django.db.models.deletion.CASCADE, related_name='options', to='web_admin.articlesurvey'), + ), + ] diff --git a/web_admin/models.py b/web_admin/models.py index 950eec6..8a3bb16 100644 --- a/web_admin/models.py +++ b/web_admin/models.py @@ -56,7 +56,7 @@ class SurveyOption(models.Model): """ Survey Options model """ - survey = models.ForeignKey(ArticleSurvey, on_delete=models.CASCADE, related_name='survey_options') + survey = models.ForeignKey(ArticleSurvey, on_delete=models.CASCADE, related_name='options') option = models.CharField(max_length=255) is_answer = models.BooleanField(default=False) created_at = models.DateTimeField(auto_now_add=True) diff --git a/web_admin/views/article.py b/web_admin/views/article.py index 5aa88b3..5b0ca92 100644 --- a/web_admin/views/article.py +++ b/web_admin/views/article.py @@ -36,7 +36,7 @@ class ArticleViewSet(GenericViewSet, mixins.CreateModelMixin, mixins.UpdateModel def get_queryset(self): article = self.queryset.objects.filter(is_deleted=False).prefetch_related( - 'article_cards', 'article_survey', 'article_survey__survey_options' + 'article_cards', 'article_survey', 'article_survey__options' ).order_by('-created_at') queryset = self.filter_queryset(article) return queryset From d3564efbb97a49829442f8c16a698bfcee186ffc Mon Sep 17 00:00:00 2001 From: abutalib-kiwi Date: Tue, 1 Aug 2023 20:13:35 +0530 Subject: [PATCH 02/61] change in update api, added method to upload and get image url --- web_admin/serializers/article_serializer.py | 42 +++++++-------------- web_admin/utils.py | 11 ++++++ 2 files changed, 24 insertions(+), 29 deletions(-) diff --git a/web_admin/serializers/article_serializer.py b/web_admin/serializers/article_serializer.py index e374679..6945880 100644 --- a/web_admin/serializers/article_serializer.py +++ b/web_admin/serializers/article_serializer.py @@ -11,7 +11,7 @@ from base.constants import (ARTICLE_SURVEY_POINTS, MAX_ARTICLE_CARD, MIN_ARTICLE from base.messages import ERROR_CODE from guardian.utils import upload_image_to_alibaba from web_admin.models import Article, ArticleCard, SurveyOption, ArticleSurvey, DefaultArticleCardImage -from web_admin.utils import pop_id +from web_admin.utils import pop_id, get_image_url USER = get_user_model() @@ -32,11 +32,7 @@ class ArticleCardSerializer(serializers.ModelSerializer): fields = ('id', 'title', 'description', 'image', 'image_url') def create(self, validated_data): - if 'image' in validated_data and validated_data['image'] is not None: - image = validated_data.pop('image') - filename = f"{ARTICLE_CARD_IMAGE_FOLDER}/{image.name}" - # upload image on ali baba - validated_data['image_url'] = upload_image_to_alibaba(image, filename) + validated_data['image_url'] = get_image_url(validated_data) article_card = ArticleCard.objects.create(article_id='1', **validated_data) return article_card @@ -111,11 +107,7 @@ class ArticleSerializer(serializers.ModelSerializer): for card in article_cards: card = pop_id(card) - if 'image' in card and card['image'] is not None: - image = card.pop('image') - filename = f"{ARTICLE_CARD_IMAGE_FOLDER}/{image.name}" - # upload image on ali baba - card['image_url'] = upload_image_to_alibaba(image, filename) + card['image_url'] = get_image_url(card) ArticleCard.objects.create(article=article, **card) for survey in article_survey: @@ -139,28 +131,22 @@ class ArticleSerializer(serializers.ModelSerializer): instance.title = validated_data.get('title', instance.title) instance.description = validated_data.get('description', instance.description) instance.save() - + prev_card = list(ArticleCard.objects.filter(article=instance).values_list('id', flat=True)) # Update or create cards for card_data in article_cards: card_id = card_data.get('id', None) if card_id: + prev_card.remove(card_id) card = ArticleCard.objects.get(id=card_id, article=instance) card.title = card_data.get('title', card.title) card.description = card_data.get('description', card.description) - if 'image' in card_data and card_data['image'] is not None: - image = card_data.pop('image') - filename = f"{ARTICLE_CARD_IMAGE_FOLDER}/{image.name}" - # upload image on ali baba - card.image_url = upload_image_to_alibaba(image, filename) + card.image_url = get_image_url(card_data) card.save() else: card_data = pop_id(card_data) - if 'image' in card_data and card_data['image'] is not None: - image = card_data.pop('image') - filename = f"{ARTICLE_CARD_IMAGE_FOLDER}/{image.name}" - # upload image on ali baba - card_data['image_url'] = upload_image_to_alibaba(image, filename) + card_data['image_url'] = get_image_url(card_data) ArticleCard.objects.create(article=instance, **card_data) + ArticleCard.objects.filter(id__in=prev_card, article=instance).delete() # Update or create survey sections for survey_data in article_survey: @@ -172,7 +158,7 @@ class ArticleSerializer(serializers.ModelSerializer): survey.save() else: survey_data = pop_id(survey_data) - survey = ArticleSurvey.objects.create(article=instance, **survey_data) + survey = ArticleSurvey.objects.create(article=instance, points=ARTICLE_SURVEY_POINTS, **survey_data) # Update or create survey options for option_data in options_data: @@ -210,6 +196,9 @@ class DefaultArticleCardImageSerializer(serializers.ModelSerializer): """ if 'image' not in attrs and attrs.get('image') is None: raise serializers.ValidationError({'details': ERROR_CODE['2061']}) + image = attrs.get('image') + if image and image.size == NUMBER['zero']: + raise serializers.ValidationError(ERROR_CODE['2035']) return attrs def create(self, validated_data): @@ -217,12 +206,7 @@ class DefaultArticleCardImageSerializer(serializers.ModelSerializer): to create and upload image :return: card_image object """ - image = validated_data.pop('image') - filename = f"{ARTICLE_CARD_IMAGE_FOLDER}/{image.name}" - if image and image.size == NUMBER['zero']: - raise serializers.ValidationError(ERROR_CODE['2035']) - # upload image on ali baba - validated_data['image_url'] = upload_image_to_alibaba(image, filename) + validated_data['image_url'] = get_image_url(validated_data) card_image = DefaultArticleCardImage.objects.create(**validated_data) return card_image diff --git a/web_admin/utils.py b/web_admin/utils.py index 2e09e2f..aea72e8 100644 --- a/web_admin/utils.py +++ b/web_admin/utils.py @@ -1,6 +1,8 @@ """ web_utils file """ +from base.constants import ARTICLE_CARD_IMAGE_FOLDER +from guardian.utils import upload_image_to_alibaba def pop_id(data): @@ -11,3 +13,12 @@ def pop_id(data): """ data.pop('id') if 'id' in data else data return data + + +def get_image_url(data): + if 'image' in data and data['image'] is not None: + image = data.pop('image') + filename = f"{ARTICLE_CARD_IMAGE_FOLDER}/{image.name}" + # upload image on ali baba + image_url = upload_image_to_alibaba(image, filename) + return image_url From f3a8b5261708bed0b49c62c73255b7ff9bf6e793 Mon Sep 17 00:00:00 2001 From: jain Date: Wed, 2 Aug 2023 11:00:02 +0530 Subject: [PATCH 03/61] api response time --- guardian/views.py | 1 - 1 file changed, 1 deletion(-) diff --git a/guardian/views.py b/guardian/views.py index 9706e87..15b29c5 100644 --- a/guardian/views.py +++ b/guardian/views.py @@ -131,7 +131,6 @@ class TaskListAPIView(viewsets.ModelViewSet): serializer_class = TaskDetailsSerializer permission_classes = [IsAuthenticated] pagination_class = PageNumberPagination - queryset = JuniorTask.objects.all() http_method_names = ('get',) def list(self, request, *args, **kwargs): From af121f5a539c628ae59a70962a2764c4d10bf84e Mon Sep 17 00:00:00 2001 From: abutalib-kiwi Date: Wed, 2 Aug 2023 11:16:12 +0530 Subject: [PATCH 04/61] changes in article update api and get image url method --- web_admin/serializers/article_serializer.py | 6 +++++- web_admin/utils.py | 4 +++- 2 files changed, 8 insertions(+), 2 deletions(-) diff --git a/web_admin/serializers/article_serializer.py b/web_admin/serializers/article_serializer.py index 6945880..f3b5d06 100644 --- a/web_admin/serializers/article_serializer.py +++ b/web_admin/serializers/article_serializer.py @@ -33,7 +33,8 @@ class ArticleCardSerializer(serializers.ModelSerializer): def create(self, validated_data): validated_data['image_url'] = get_image_url(validated_data) - article_card = ArticleCard.objects.create(article_id='1', **validated_data) + article = Article.objects.all().first() + article_card = ArticleCard.objects.create(article=article, **validated_data) return article_card @@ -148,11 +149,13 @@ class ArticleSerializer(serializers.ModelSerializer): ArticleCard.objects.create(article=instance, **card_data) ArticleCard.objects.filter(id__in=prev_card, article=instance).delete() + prev_survey = list(ArticleSurvey.objects.filter(article=instance).values_list('id', flat=True)) # Update or create survey sections for survey_data in article_survey: survey_id = survey_data.get('id', None) options_data = survey_data.pop('options') if survey_id: + prev_survey.remove(survey_id) survey = ArticleSurvey.objects.get(id=survey_id, article=instance) survey.question = survey_data.get('question', survey.question) survey.save() @@ -171,6 +174,7 @@ class ArticleSerializer(serializers.ModelSerializer): else: option_data = pop_id(option_data) SurveyOption.objects.create(survey=survey, **option_data) + ArticleSurvey.objects.filter(id__in=prev_survey, article=instance).delete() return instance diff --git a/web_admin/utils.py b/web_admin/utils.py index aea72e8..6ecd5e3 100644 --- a/web_admin/utils.py +++ b/web_admin/utils.py @@ -16,7 +16,9 @@ def pop_id(data): def get_image_url(data): - if 'image' in data and data['image'] is not None: + if 'image_url' in data: + return data['image_url'] + elif 'image' in data and data['image'] is not None: image = data.pop('image') filename = f"{ARTICLE_CARD_IMAGE_FOLDER}/{image.name}" # upload image on ali baba From ed8fc156ac3946d8c15f3c79ae68dbd99784081b Mon Sep 17 00:00:00 2001 From: jain Date: Wed, 2 Aug 2023 14:23:36 +0530 Subject: [PATCH 05/61] change junior task list api response --- base/constants.py | 1 + guardian/serializers.py | 27 ++++++++++++++++++++++++++- junior/serializers.py | 8 ++++++-- junior/views.py | 6 +++--- 4 files changed, 36 insertions(+), 6 deletions(-) diff --git a/base/constants.py b/base/constants.py index 3c1501c..6022f62 100644 --- a/base/constants.py +++ b/base/constants.py @@ -83,6 +83,7 @@ IN_PROGRESS = 2 REJECTED = 3 REQUESTED = 4 COMPLETED = 5 +EXPIRED = 6 TASK_POINTS = 5 # duplicate name used defined in constant PROJECT_NAME PROJECT_NAME = 'Zod Bank' diff --git a/guardian/serializers.py b/guardian/serializers.py index df2178a..1509fdf 100644 --- a/guardian/serializers.py +++ b/guardian/serializers.py @@ -211,7 +211,7 @@ class GuardianDetailSerializer(serializers.ModelSerializer): """Meta info""" model = Guardian fields = ['id', 'email', 'first_name', 'last_name', 'country_code', 'phone', 'gender', 'dob', - 'guardian_code', 'referral_code','is_active', 'is_complete_profile', 'created_at', 'image', + 'guardian_code','is_active', 'is_complete_profile', 'created_at', 'image', 'updated_at'] class TaskDetailsSerializer(serializers.ModelSerializer): """Task detail serializer""" @@ -244,6 +244,31 @@ class TaskDetailsSerializer(serializers.ModelSerializer): 'requested_on', 'rejected_on', 'completed_on', 'is_expired', 'junior', 'task_status', 'is_active', 'remaining_time', 'created_at','updated_at'] +class TaskDetailsjuniorSerializer(serializers.ModelSerializer): + """Task detail serializer""" + + guardian = GuardianDetailSerializer() + 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', + 'requested_on', 'rejected_on', 'completed_on', + 'junior', 'task_status', 'is_active', 'remaining_time', 'created_at','updated_at'] class TopJuniorSerializer(serializers.ModelSerializer): """Top junior serializer""" diff --git a/junior/serializers.py b/junior/serializers.py index 27a6808..0c5911a 100644 --- a/junior/serializers.py +++ b/junior/serializers.py @@ -12,7 +12,7 @@ from account.utils import send_otp_email, generate_code from junior.models import Junior, JuniorPoints, JuniorGuardianRelationship 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 +from base.constants import PENDING, IN_PROGRESS, REJECTED, REQUESTED, COMPLETED, NUMBER, JUN, ZOD, EXPIRED from guardian.models import Guardian, JuniorTask from account.models import UserEmailOtp, UserNotification from junior.utils import junior_notification_email, junior_approval_mail @@ -335,6 +335,7 @@ class JuniorPointsSerializer(serializers.ModelSerializer): requested_task = serializers.SerializerMethodField('get_requested_task') rejected_task = serializers.SerializerMethodField('get_rejected_task') pending_task = serializers.SerializerMethodField('get_pending_task') + expired_task = serializers.SerializerMethodField('get_expired_task') position = serializers.SerializerMethodField('get_position') def get_junior_id(self, obj): @@ -370,11 +371,14 @@ class JuniorPointsSerializer(serializers.ModelSerializer): def get_pending_task(self, obj): return JuniorTask.objects.filter(junior=obj.junior, task_status=PENDING).count() + def get_expired_task(self, obj): + return JuniorTask.objects.filter(junior=obj.junior, task_status=EXPIRED).count() + class Meta(object): """Meta info""" model = Junior fields = ['junior_id', 'total_points', 'position', 'pending_task', 'in_progress_task', 'completed_task', - 'requested_task', 'rejected_task'] + 'requested_task', 'rejected_task', 'expired_task'] class AddGuardianSerializer(serializers.ModelSerializer): """Add guardian serializer""" diff --git a/junior/views.py b/junior/views.py index c9c8739..8cb95a3 100644 --- a/junior/views.py +++ b/junior/views.py @@ -32,7 +32,7 @@ from .serializers import (CreateJuniorSerializer, JuniorDetailListSerializer, Ad RemoveJuniorSerializer, CompleteTaskSerializer, JuniorPointsSerializer, AddGuardianSerializer, StartTaskSerializer) from guardian.models import Guardian, JuniorTask -from guardian.serializers import TaskDetailsSerializer +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 @@ -265,7 +265,7 @@ class RemoveJuniorAPIView(views.APIView): class JuniorTaskListAPIView(viewsets.ModelViewSet): """Update guardian profile""" - serializer_class = TaskDetailsSerializer + serializer_class = TaskDetailsjuniorSerializer permission_classes = [IsAuthenticated] pagination_class = PageNumberPagination queryset = JuniorTask.objects.all() @@ -295,7 +295,7 @@ class JuniorTaskListAPIView(viewsets.ModelViewSet): # use Pagination paginated_queryset = paginator.paginate_queryset(queryset, request) # use TaskDetailsSerializer serializer - serializer = TaskDetailsSerializer(paginated_queryset, many=True) + serializer = TaskDetailsjuniorSerializer(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) From ebb468166edc39c6dc0761e7d42bf1969dcd5f7e Mon Sep 17 00:00:00 2001 From: jain Date: Wed, 2 Aug 2023 15:20:43 +0530 Subject: [PATCH 06/61] sprint4 reassign task after expired --- base/messages.py | 6 ++++-- junior/serializers.py | 14 ++++++++++++++ junior/urls.py | 5 +++-- junior/views.py | 26 +++++++++++++++++++++++++- 4 files changed, 46 insertions(+), 5 deletions(-) diff --git a/base/messages.py b/base/messages.py index 0407719..bff508d 100644 --- a/base/messages.py +++ b/base/messages.py @@ -91,7 +91,8 @@ ERROR_CODE = { "2062": "Please enter email address", "2063": "Unauthorized access.", "2064": "To change your password first request an OTP and get it verify then change your password.", - "2065": "Passwords do not match. Please try again." + "2065": "Passwords do not match. Please try again.", + "2066": "Task does not exist or not in expired state" } """Success message code""" SUCCESS_CODE = { @@ -140,7 +141,8 @@ SUCCESS_CODE = { "3032": "Task request sent successfully", "3033": "Valid Referral code", "3034": "Invite guardian successfully", - "3035": "Task started successfully" + "3035": "Task started successfully", + "3036": "Task reassign successfully" } """status code error""" STATUS_CODE_ERROR = { diff --git a/junior/serializers.py b/junior/serializers.py index 0c5911a..54332f0 100644 --- a/junior/serializers.py +++ b/junior/serializers.py @@ -458,3 +458,17 @@ class StartTaskSerializer(serializers.ModelSerializer): instance.task_status = str(NUMBER['two']) instance.save() return instance + +class ReAssignTaskSerializer(serializers.ModelSerializer): + """User task Serializer""" + class Meta(object): + """Meta class""" + model = JuniorTask + fields = ('id', 'due_date') + def update(self, instance, validated_data): + instance.task_status = str(NUMBER['one']) + instance.due_date = validated_data.get('due_date') + instance.is_approved = False + instance.requested_on = None + instance.save() + return instance diff --git a/junior/urls.py b/junior/urls.py index e4b2489..f66fa30 100644 --- a/junior/urls.py +++ b/junior/urls.py @@ -4,7 +4,7 @@ from django.urls import path, include from .views import (UpdateJuniorProfile, ValidateGuardianCode, JuniorListAPIView, AddJuniorAPIView, InvitedJuniorAPIView, FilterJuniorAPIView, RemoveJuniorAPIView, JuniorTaskListAPIView, CompleteJuniorTaskAPIView, JuniorPointsListAPIView, ValidateReferralCode, - InviteGuardianAPIView, StartTaskAPIView) + InviteGuardianAPIView, StartTaskAPIView, ReAssignJuniorTaskAPIView) """Third party import""" from rest_framework import routers @@ -46,5 +46,6 @@ urlpatterns = [ path('api/v1/', include(router.urls)), path('api/v1/remove-junior/', RemoveJuniorAPIView.as_view()), path('api/v1/complete-task/', CompleteJuniorTaskAPIView.as_view()), - path('api/v1/start-task/', StartTaskAPIView.as_view()) + path('api/v1/start-task/', StartTaskAPIView.as_view()), + path('api/v1/reassign-task/', ReAssignJuniorTaskAPIView.as_view()), ] diff --git a/junior/views.py b/junior/views.py index 8cb95a3..14920ec 100644 --- a/junior/views.py +++ b/junior/views.py @@ -30,7 +30,7 @@ import requests from junior.models import Junior, JuniorPoints from .serializers import (CreateJuniorSerializer, JuniorDetailListSerializer, AddJuniorSerializer,\ RemoveJuniorSerializer, CompleteTaskSerializer, JuniorPointsSerializer, - AddGuardianSerializer, StartTaskSerializer) + AddGuardianSerializer, StartTaskSerializer, ReAssignTaskSerializer) from guardian.models import Guardian, JuniorTask from guardian.serializers import TaskDetailsSerializer, TaskDetailsjuniorSerializer from base.messages import ERROR_CODE, SUCCESS_CODE @@ -434,3 +434,27 @@ class StartTaskAPIView(views.APIView): return custom_error_response(ERROR_CODE['2060'], 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 ReAssignJuniorTaskAPIView(views.APIView): + """Update junior task API""" + serializer_class = ReAssignTaskSerializer + model = JuniorTask + permission_classes = [IsAuthenticated] + + def put(self, request, format=None): + try: + task_id = self.request.data.get('task_id') + task_queryset = JuniorTask.objects.filter(id=task_id, guardian__user__email=self.request.user).last() + if task_queryset and task_queryset.task_status == str(NUMBER['six']): + # use StartTaskSerializer serializer + serializer = ReAssignTaskSerializer(task_queryset, data=request.data, partial=True) + if serializer.is_valid(): + # save serializer + serializer.save() + return custom_response(SUCCESS_CODE['3036'], 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['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) From 75d0b12008ff4ca616d2ea11d6ca254b47f15d45 Mon Sep 17 00:00:00 2001 From: jain Date: Wed, 2 Aug 2023 16:26:20 +0530 Subject: [PATCH 07/61] changes in remove guardian code api --- guardian/serializers.py | 2 +- junior/models.py | 2 ++ junior/serializers.py | 2 +- junior/views.py | 3 +-- 4 files changed, 5 insertions(+), 4 deletions(-) diff --git a/guardian/serializers.py b/guardian/serializers.py index 1509fdf..db465ad 100644 --- a/guardian/serializers.py +++ b/guardian/serializers.py @@ -345,7 +345,7 @@ class ApproveJuniorSerializer(serializers.ModelSerializer): """update guardian code""" instance = self.context['junior'] instance.guardian_code = [self.context['guardian_code']] - instance.is_invited = True + instance.guardian_code_approved = True instance.save() return instance diff --git a/junior/models.py b/junior/models.py index dc71c97..b18cbf5 100644 --- a/junior/models.py +++ b/junior/models.py @@ -71,6 +71,8 @@ class Junior(models.Model): 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) # Profile created and updated time""" created_at = models.DateTimeField(auto_now_add=True) updated_at = models.DateTimeField(auto_now=True) diff --git a/junior/serializers.py b/junior/serializers.py index 54332f0..316c1bf 100644 --- a/junior/serializers.py +++ b/junior/serializers.py @@ -246,7 +246,7 @@ 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'] + 'is_invited', 'passcode', 'guardian_code_approved'] class AddJuniorSerializer(serializers.ModelSerializer): """Add junior serializer""" diff --git a/junior/views.py b/junior/views.py index 14920ec..3f3cd5a 100644 --- a/junior/views.py +++ b/junior/views.py @@ -246,8 +246,7 @@ 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), - is_invited=True).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) From 404825dc858d6e346c50efe68af3f8ac397b03cc Mon Sep 17 00:00:00 2001 From: jain Date: Wed, 2 Aug 2023 16:57:40 +0530 Subject: [PATCH 08/61] migration file --- ...018_remove_junior_relationship_and_more.py | 22 +++++++++++++++++++ 1 file changed, 22 insertions(+) create mode 100644 junior/migrations/0018_remove_junior_relationship_and_more.py diff --git a/junior/migrations/0018_remove_junior_relationship_and_more.py b/junior/migrations/0018_remove_junior_relationship_and_more.py new file mode 100644 index 0000000..d24b43f --- /dev/null +++ b/junior/migrations/0018_remove_junior_relationship_and_more.py @@ -0,0 +1,22 @@ +# Generated by Django 4.2.2 on 2023-08-02 11:27 + +from django.db import migrations, models + + +class Migration(migrations.Migration): + + dependencies = [ + ('junior', '0017_juniorguardianrelationship'), + ] + + operations = [ + migrations.RemoveField( + model_name='junior', + name='relationship', + ), + migrations.AddField( + model_name='junior', + name='guardian_code_approved', + field=models.BooleanField(default=False), + ), + ] From 685f62770752ac8a136cf95ea6412a097a6207f3 Mon Sep 17 00:00:00 2001 From: abutalib-kiwi Date: Wed, 2 Aug 2023 19:16:29 +0530 Subject: [PATCH 09/61] change method to get real time --- guardian/serializers.py | 21 ++++++++++++--------- junior/serializers.py | 15 +++++++++++---- 2 files changed, 23 insertions(+), 13 deletions(-) diff --git a/guardian/serializers.py b/guardian/serializers.py index c81744a..4d0ea65 100644 --- a/guardian/serializers.py +++ b/guardian/serializers.py @@ -1,5 +1,5 @@ """Serializer of Guardian""" -"""Third party Django app""" +# third party imports import logging from rest_framework import serializers # Import Refresh token of jwt @@ -7,7 +7,8 @@ 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 pytz +from django.utils import timezone # Import guardian's model, # Import junior's model, # Import account's model, @@ -16,7 +17,7 @@ from datetime import datetime, time # Import messages from # base package, # Import some functions -# from utils file""" +# local imports from .models import Guardian, JuniorTask from account.models import UserProfile, UserEmailOtp, UserNotification from account.utils import generate_code @@ -222,12 +223,9 @@ class TaskDetailsSerializer(serializers.ModelSerializer): def get_remaining_time(self, obj): """ remaining time to complete task""" - import pytz - from django.utils import timezone due_date_datetime = datetime.combine(obj.due_date, datetime.max.time()) # fetch real time # current_datetime = real_time() - print(due_date_datetime.astimezone(pytz.utc), datetime.now(pytz.utc), timezone.now().astimezone(pytz.utc)) # new code due_date_datetime = due_date_datetime.astimezone(pytz.utc) current_datetime = timezone.now().astimezone(pytz.utc) @@ -260,7 +258,10 @@ class TaskDetailsjuniorSerializer(serializers.ModelSerializer): """ remaining time to complete task""" due_date_datetime = datetime.combine(obj.due_date, datetime.max.time()) # fetch real time - current_datetime = real_time() + # current_datetime = real_time() + # new code + due_date_datetime = due_date_datetime.astimezone(pytz.utc) + current_datetime = timezone.now().astimezone(pytz.utc) # Perform the subtraction if due_date_datetime > current_datetime: time_difference = due_date_datetime - current_datetime @@ -377,7 +378,8 @@ class ApproveTaskSerializer(serializers.ModelSerializer): # update total task point junior_data.total_points = junior_data.total_points + instance.points # update complete time of task - instance.completed_on = real_time() + # instance.completed_on = real_time() + instance.completed_on = timezone.now().astimezone(pytz.utc) send_notification.delay(TASK_POINTS, None, junior_details.auth.id, {}) else: # reject the task @@ -386,7 +388,8 @@ class ApproveTaskSerializer(serializers.ModelSerializer): # 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 = real_time() + instance.rejected_on = timezone.now().astimezone(pytz.utc) send_notification.delay(TASK_REJECTED, None, junior_details.auth.id, {}) instance.save() junior_data.save() diff --git a/junior/serializers.py b/junior/serializers.py index 1dc04da..bdedbb5 100644 --- a/junior/serializers.py +++ b/junior/serializers.py @@ -1,5 +1,8 @@ """Serializer file for junior""" -"""Import Django 3rd party app""" +# third party imports +import pytz + +# django imports from rest_framework import serializers from django.contrib.auth.models import User from django.db import transaction @@ -7,7 +10,7 @@ from datetime import datetime from django.utils import timezone from rest_framework_simplejwt.tokens import RefreshToken -"""Import django app""" +# local imports from account.utils import send_otp_email, generate_code from junior.models import Junior, JuniorPoints, JuniorGuardianRelationship from guardian.tasks import generate_otp @@ -320,7 +323,8 @@ 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 = real_time() + instance.requested_on = timezone.now().astimezone(pytz.utc) instance.task_status = str(NUMBER['four']) instance.is_approved = False instance.save() @@ -445,7 +449,10 @@ class StartTaskSerializer(serializers.ModelSerializer): """ remaining time to complete task""" due_date = datetime.combine(obj.due_date, datetime.max.time()) # fetch real time - real_datetime = real_time() + # real_datetime = real_time() + # new code + due_date = due_date.astimezone(pytz.utc) + real_datetime = timezone.now().astimezone(pytz.utc) # Perform the subtraction if due_date > real_datetime: time_difference = due_date - real_datetime From f3e2ab9a346ca28e3acc7b9b37db6fc32167906b Mon Sep 17 00:00:00 2001 From: abutalib-kiwi Date: Wed, 2 Aug 2023 20:45:46 +0530 Subject: [PATCH 10/61] notification list modified and mark as read method changed --- notifications/serializers.py | 4 +++- notifications/views.py | 25 +++++++++++++++++++++---- 2 files changed, 24 insertions(+), 5 deletions(-) diff --git a/notifications/serializers.py b/notifications/serializers.py index ff05dcd..a061369 100644 --- a/notifications/serializers.py +++ b/notifications/serializers.py @@ -8,6 +8,7 @@ from rest_framework import serializers from notifications.utils import register_fcm_token from notifications.models import Notification + class RegisterDevice(serializers.Serializer): """ used to create and validate register device token @@ -27,7 +28,8 @@ class RegisterDevice(serializers.Serializer): return register_fcm_token(self.context['user_id'], registration_id, validated_data['device_id'], device_type) -class NotificationListSerailizer(serializers.ModelSerializer): + +class NotificationListSerializer(serializers.ModelSerializer): """List of notification""" class Meta(object): diff --git a/notifications/views.py b/notifications/views.py index 3d2ff4a..4644e62 100644 --- a/notifications/views.py +++ b/notifications/views.py @@ -11,15 +11,32 @@ from rest_framework import viewsets, status, views from account.utils import custom_response, custom_error_response from base.messages import SUCCESS_CODE, ERROR_CODE from notifications.constants import TEST_NOTIFICATION -from notifications.serializers import RegisterDevice, NotificationListSerailizer, ReadNotificationSerializer +from notifications.serializers import RegisterDevice, NotificationListSerializer, ReadNotificationSerializer from notifications.utils import send_notification from notifications.models import Notification + class NotificationViewSet(viewsets.GenericViewSet): """ used to do the notification actions """ - serializer_class = RegisterDevice + serializer_class = NotificationListSerializer permission_classes = [IsAuthenticated, ] + def list(self, request, *args, **kwargs) -> Response: + """ list the notifications """ + queryset = Notification.objects.filter(notification_to_id=request.auth.payload['user_id'] + ).select_related('notification_to').order_by('-id') + 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): """ @@ -41,14 +58,14 @@ class NotificationViewSet(viewsets.GenericViewSet): return custom_response(SUCCESS_CODE["3000"]) @action(methods=['get'], detail=False, url_path='list', url_name='list', - serializer_class=NotificationListSerailizer) + serializer_class=NotificationListSerializer) def notification_list(self, request): """ notification list """ try: queryset = Notification.objects.filter(notification_to=request.user) - serializer = NotificationListSerailizer(queryset, many=True) + 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) From 3806d1f3a6980eed807bd110cc0499aad8e0d06a Mon Sep 17 00:00:00 2001 From: abutalib-kiwi Date: Thu, 3 Aug 2023 18:12:44 +0530 Subject: [PATCH 11/61] user detail api, changed image upload method --- celerybeat-schedule | Bin 16384 -> 16384 bytes guardian/utils.py | 5 +- web_admin/serializers/article_serializer.py | 54 +------ .../serializers/user_management_serializer.py | 135 ++++++++++++++++++ web_admin/urls.py | 5 +- web_admin/utils.py | 18 ++- web_admin/views/article.py | 30 +--- web_admin/views/user_management.py | 58 ++++++++ 8 files changed, 220 insertions(+), 85 deletions(-) create mode 100644 web_admin/serializers/user_management_serializer.py create mode 100644 web_admin/views/user_management.py diff --git a/celerybeat-schedule b/celerybeat-schedule index 71062ba3971bec6320e397a68913989f4b8aa458..4a51b891af30ec5d0a7f9b91777d2f6b8b1a45d1 100644 GIT binary patch delta 32 ocmZo@U~Fh$+_1@rU53R}nCpA=;j7*bt4K+8L7@y$*0IpsNU;qFB delta 32 ocmZo@U~Fh$+_1@rU7A&xg+n%K@@<1aM#jmyhMJpAjL+}@0Gr?mQ2+n{ diff --git a/guardian/utils.py b/guardian/utils.py index 920a119..9a6ae05 100644 --- a/guardian/utils.py +++ b/guardian/utils.py @@ -41,7 +41,10 @@ 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""" - temp_file.write(image.read()) + 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""" diff --git a/web_admin/serializers/article_serializer.py b/web_admin/serializers/article_serializer.py index f3b5d06..b7dbf69 100644 --- a/web_admin/serializers/article_serializer.py +++ b/web_admin/serializers/article_serializer.py @@ -21,8 +21,8 @@ class ArticleCardSerializer(serializers.ModelSerializer): Article Card serializer """ id = serializers.IntegerField(required=False) - image = serializers.FileField(required=False) - image_url = serializers.URLField(required=False) + image_name = serializers.CharField(required=False) + image_url = serializers.CharField(required=False) class Meta: """ @@ -214,53 +214,3 @@ class DefaultArticleCardImageSerializer(serializers.ModelSerializer): card_image = DefaultArticleCardImage.objects.create(**validated_data) return card_image - - -class UserManagementListSerializer(serializers.ModelSerializer): - """ - user management serializer - """ - name = serializers.SerializerMethodField() - phone_number = serializers.SerializerMethodField() - user_type = serializers.SerializerMethodField() - - class Meta: - """ - meta class - """ - model = USER - fields = ('name', 'email', 'phone_number', 'user_type', 'is_active') - - @staticmethod - def get_name(obj): - """ - :param obj: user object - :return: full name - """ - return (obj.first_name + obj.last_name) if obj.last_name else obj.first_name - - @staticmethod - def get_phone_number(obj): - """ - :param obj: user object - :return: user phone number - """ - if profile := obj.guardian_profile.all().first(): - return profile.phone - elif profile := obj.junior_profile.all().first(): - return 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') - elif obj.junior_profile.all().first(): - return dict(USER_TYPE).get('1') - else: - return None diff --git a/web_admin/serializers/user_management_serializer.py b/web_admin/serializers/user_management_serializer.py new file mode 100644 index 0000000..9e1920b --- /dev/null +++ b/web_admin/serializers/user_management_serializer.py @@ -0,0 +1,135 @@ +""" +web_admin user_management serializers file +""" +# django imports +from rest_framework import serializers +from django.contrib.auth import get_user_model + +from base.constants import (ARTICLE_SURVEY_POINTS, MAX_ARTICLE_CARD, MIN_ARTICLE_SURVEY, MAX_ARTICLE_SURVEY, NUMBER, + USER_TYPE, ARTICLE_CARD_IMAGE_FOLDER) +# local imports +from base.messages import ERROR_CODE +from guardian.models import Guardian +from guardian.utils import upload_image_to_alibaba +from junior.models import Junior +from web_admin.models import Article, ArticleCard, SurveyOption, ArticleSurvey, DefaultArticleCardImage +from web_admin.utils import pop_id, get_image_url + +USER = get_user_model() + + +class UserManagementListSerializer(serializers.ModelSerializer): + """ + user management serializer + """ + name = serializers.SerializerMethodField() + phone_number = serializers.SerializerMethodField() + user_type = serializers.SerializerMethodField() + + class Meta: + """ + meta class + """ + model = USER + fields = ('id', 'name', 'email', 'phone_number', 'user_type', 'is_active') + + @staticmethod + def get_name(obj): + """ + :param obj: user object + :return: full name + """ + return f"{obj.first_name} {obj.last_name}" if obj.last_name else obj.first_name + + @staticmethod + def get_phone_number(obj): + """ + :param obj: user object + :return: user phone number + """ + if profile := obj.guardian_profile.all().first(): + return f"+{profile.country_code}{profile.phone}" if profile.country_code and profile.phone else profile.phone + elif profile := 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') + elif obj.junior_profile.all().first(): + return dict(USER_TYPE).get('1') + else: + return None + + +class GuardianSerializer(serializers.ModelSerializer): + name = serializers.SerializerMethodField() + + class Meta: + model = Guardian + fields = ('name', 'dob', 'gender', 'phone', 'is_active', 'country_name', 'image') + + @staticmethod + def get_name(obj): + """ + :param obj: user object + :return: full name + """ + return f"{obj.user.first_name} {obj.user.last_name}" if obj.user.last_name else obj.user.first_name + + +class JuniorSerializer(serializers.ModelSerializer): + name = serializers.SerializerMethodField() + + class Meta: + model = Junior + fields = ('name', 'dob', 'gender', 'phone', 'is_active', 'country_name', 'image') + + @staticmethod + def get_name(obj): + """ + :param obj: user object + :return: full name + """ + return f"{obj.auth.first_name} {obj.auth.last_name}" if obj.auth.last_name else obj.auth.first_name + + +class UserManagementDetailSerializer(serializers.ModelSerializer): + user_type = serializers.SerializerMethodField() + guardian_profile = GuardianSerializer(many=True) + junior_profile = JuniorSerializer(many=True) + associated_users = serializers.SerializerMethodField() + + class Meta: + """ + meta class + """ + model = USER + fields = ('user_type', 'email', 'guardian_profile', 'junior_profile', 'associated_users') + + @staticmethod + def get_user_type(obj): + """ + :param obj: user object + :return: user type + """ + profile = obj.guardian_profile.all().first() + 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 + + @staticmethod + def get_associated_users(obj): + if profile := obj.guardian_profile.all().first(): + junior = Junior.objects.filter(guardian_code__contains=[profile.guardian_code], is_verified=True) + serializer = JuniorSerializer(junior, many=True) + return serializer.data diff --git a/web_admin/urls.py b/web_admin/urls.py index 5fbe21e..30645a2 100644 --- a/web_admin/urls.py +++ b/web_admin/urls.py @@ -6,15 +6,16 @@ from django.urls import path, include from rest_framework import routers # local imports -from web_admin.views.article import ArticleViewSet, DefaultArticleCardImagesViewSet, UserManagementViewSet +from web_admin.views.article import ArticleViewSet, DefaultArticleCardImagesViewSet from web_admin.views.auth import ForgotAndResetPasswordViewSet +from web_admin.views.user_management import UserManagementViewSet # initiate router router = routers.SimpleRouter() router.register('article', ArticleViewSet, basename='article') router.register('default-card-images', DefaultArticleCardImagesViewSet, basename='default-card-images') -router.register('user', UserManagementViewSet, basename='user') +router.register('user-management', UserManagementViewSet, basename='user') # forgot and reset password api for admin router.register('admin', ForgotAndResetPasswordViewSet, basename='admin') diff --git a/web_admin/utils.py b/web_admin/utils.py index 6ecd5e3..6cdc240 100644 --- a/web_admin/utils.py +++ b/web_admin/utils.py @@ -1,6 +1,8 @@ """ web_utils file """ +import base64 + from base.constants import ARTICLE_CARD_IMAGE_FOLDER from guardian.utils import upload_image_to_alibaba @@ -16,8 +18,22 @@ def pop_id(data): def get_image_url(data): - if 'image_url' in data: + """ + to get image url + :param data: + :return: image url + """ + if 'image_url' in data and 'http' in data['image_url']: + if 'image_name' in data: + data.pop('image_name') 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'] + filename = f"{ARTICLE_CARD_IMAGE_FOLDER}/{image_name}" + # upload image on ali baba + image_url = upload_image_to_alibaba(base64_image, filename) + return image_url elif 'image' in data and data['image'] is not None: image = data.pop('image') filename = f"{ARTICLE_CARD_IMAGE_FOLDER}/{image.name}" diff --git a/web_admin/views/article.py b/web_admin/views/article.py index 5b0ca92..b8f8a1f 100644 --- a/web_admin/views/article.py +++ b/web_admin/views/article.py @@ -16,8 +16,7 @@ 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, - UserManagementListSerializer) + DefaultArticleCardImageSerializer) USER = get_user_model() @@ -195,30 +194,3 @@ class DefaultArticleCardImagesViewSet(GenericViewSet, mixins.CreateModelMixin, m return custom_response(None, data=serializer.data) -class UserManagementViewSet(GenericViewSet, mixins.ListModelMixin): - """ - api to manage (list, view, edit) user - """ - serializer_class = UserManagementListSerializer - permission_classes = [] - queryset = USER.objects.prefetch_related( - 'guardian_profile', 'junior_profile') - - def get_queryset(self): - if self.request.query_params.get('user_type') == dict(USER_TYPE).get('2'): - return self.queryset.filter(junior_profile__isnull=True) - elif self.request.query_params.get('user_type') == dict(USER_TYPE).get('1'): - return self.queryset.filter(guardian_profile__isnull=True) - else: - return self.queryset - - def list(self, request, *args, **kwargs): - """ - api method to list all the user - :param request: - :return: - """ - queryset = self.get_queryset() - serializer = self.serializer_class(queryset, many=True) - return custom_response(None, data=serializer.data) - diff --git a/web_admin/views/user_management.py b/web_admin/views/user_management.py new file mode 100644 index 0000000..ffca49d --- /dev/null +++ b/web_admin/views/user_management.py @@ -0,0 +1,58 @@ +""" +web_admin user_management 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.decorators import action +from rest_framework.permissions import IsAuthenticated, AllowAny +from django.contrib.auth import get_user_model + +# local imports +from account.utils import custom_response, custom_error_response +from base.constants import USER_TYPE +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.user_management_serializer import (UserManagementListSerializer, + UserManagementDetailSerializer) + +USER = get_user_model() + + +class UserManagementViewSet(GenericViewSet, mixins.ListModelMixin, mixins.RetrieveModelMixin): + """ + api to manage (list, view, edit) user + """ + serializer_class = UserManagementListSerializer + permission_classes = [] + queryset = USER.objects.filter(is_superuser=False).prefetch_related( + 'guardian_profile', 'junior_profile') + + def get_queryset(self): + if self.request.query_params.get('user_type') == dict(USER_TYPE).get('2'): + return self.queryset.filter(junior_profile__isnull=True) + elif self.request.query_params.get('user_type') == dict(USER_TYPE).get('1'): + return self.queryset.filter(guardian_profile__isnull=True) + else: + return self.queryset + + def list(self, request, *args, **kwargs): + """ + api method to list all the user + :param request: + :return: + """ + queryset = self.get_queryset() + serializer = self.serializer_class(queryset, many=True) + return custom_response(None, data=serializer.data) + + def retrieve(self, request, *args, **kwargs): + 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) + return custom_response(None, data=serializer.data) From dd0f2b027a84d9e510c6c1fd3b94da3dfef65dab Mon Sep 17 00:00:00 2001 From: abutalib-kiwi Date: Thu, 3 Aug 2023 18:17:01 +0530 Subject: [PATCH 12/61] task list api modified --- guardian/serializers.py | 8 ++++- guardian/views.py | 78 +++++++++++++++++++++++++++++------------ 2 files changed, 63 insertions(+), 23 deletions(-) diff --git a/guardian/serializers.py b/guardian/serializers.py index db465ad..d92b637 100644 --- a/guardian/serializers.py +++ b/guardian/serializers.py @@ -222,9 +222,15 @@ class TaskDetailsSerializer(serializers.ModelSerializer): def get_remaining_time(self, obj): """ remaining time to complete task""" + import pytz + from django.utils import timezone due_date_datetime = datetime.combine(obj.due_date, datetime.max.time()) # fetch real time - current_datetime = real_time() + # current_datetime = real_time() + print(due_date_datetime.astimezone(pytz.utc), datetime.now(pytz.utc), timezone.now().astimezone(pytz.utc)) + # new code + due_date_datetime = due_date_datetime.astimezone(pytz.utc) + current_datetime = timezone.now().astimezone(pytz.utc) # Perform the subtraction if due_date_datetime > current_datetime: time_difference = due_date_datetime - current_datetime diff --git a/guardian/views.py b/guardian/views.py index 15b29c5..612f837 100644 --- a/guardian/views.py +++ b/guardian/views.py @@ -10,6 +10,8 @@ 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 rest_framework.filters import SearchFilter + from django.utils import timezone # Import guardian's model, @@ -126,37 +128,69 @@ 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""" serializer_class = TaskDetailsSerializer permission_classes = [IsAuthenticated] + filter_backends = (SearchFilter,) + search_fields = ['task_name', ] pagination_class = PageNumberPagination http_method_names = ('get',) + def get_queryset(self): + queryset = JuniorTask.objects.filter(guardian__user=self.request.user + ).prefetch_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""" - 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) + status_value = self.request.GET.get('status') + queryset = self.get_queryset() + if status_value and status_value != '0': + queryset = queryset.filter(task_status=status_value) + paginator = self.pagination_class() + # 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) + class CreateTaskAPIView(viewsets.ModelViewSet): """create task for junior""" From b9e2d9bc8a643cee6fe68accd1a7ef9b9e534b3c Mon Sep 17 00:00:00 2001 From: abutalib-kiwi Date: Thu, 3 Aug 2023 18:35:19 +0530 Subject: [PATCH 13/61] user detail api, changed image upload method --- web_admin/serializers/article_serializer.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/web_admin/serializers/article_serializer.py b/web_admin/serializers/article_serializer.py index b7dbf69..427f20d 100644 --- a/web_admin/serializers/article_serializer.py +++ b/web_admin/serializers/article_serializer.py @@ -29,7 +29,7 @@ class ArticleCardSerializer(serializers.ModelSerializer): meta class """ model = ArticleCard - fields = ('id', 'title', 'description', 'image', 'image_url') + fields = ('id', 'title', 'description', 'image_name', 'image_url') def create(self, validated_data): validated_data['image_url'] = get_image_url(validated_data) From 756bea047129cdeec2462028fa5c30e0e104ff54 Mon Sep 17 00:00:00 2001 From: abutalib-kiwi Date: Fri, 4 Aug 2023 13:15:32 +0530 Subject: [PATCH 14/61] user detail view api, searching user list, count --- account/utils.py | 6 ++- .../serializers/user_management_serializer.py | 42 +++++++++++++++++-- web_admin/views/article.py | 3 +- web_admin/views/user_management.py | 25 +++++++---- 4 files changed, 63 insertions(+), 13 deletions(-) diff --git a/account/utils.py b/account/utils.py index d1e6e54..4dc6acc 100644 --- a/account/utils.py +++ b/account/utils.py @@ -138,12 +138,14 @@ def send_support_email(name, sender, subject, message): } ) return name -def custom_response(detail, data=None, response_status=status.HTTP_200_OK): + + +def custom_response(detail, data=None, response_status=status.HTTP_200_OK, count=None): """Custom response code""" if not data: """when data is none""" data = None - return Response({"data": data, "message": detail, "status": "success", "code": response_status}) + return Response({"data": data, "message": detail, "status": "success", "code": response_status, "count": count}) def custom_error_response(detail, response_status): diff --git a/web_admin/serializers/user_management_serializer.py b/web_admin/serializers/user_management_serializer.py index 9e1920b..5293cc4 100644 --- a/web_admin/serializers/user_management_serializer.py +++ b/web_admin/serializers/user_management_serializer.py @@ -25,6 +25,7 @@ class UserManagementListSerializer(serializers.ModelSerializer): name = serializers.SerializerMethodField() phone_number = serializers.SerializerMethodField() user_type = serializers.SerializerMethodField() + is_active = serializers.SerializerMethodField() class Meta: """ @@ -67,13 +68,27 @@ class UserManagementListSerializer(serializers.ModelSerializer): else: return None + @staticmethod + def get_is_active(obj): + """ + :param obj: user object + :return: user type + """ + if profile := obj.guardian_profile.all().first(): + return profile.is_active + elif profile := obj.junior_profile.all().first(): + return profile.is_active + else: + return obj.is_active + class GuardianSerializer(serializers.ModelSerializer): name = serializers.SerializerMethodField() + phone_number = serializers.SerializerMethodField() class Meta: model = Guardian - fields = ('name', 'dob', 'gender', 'phone', 'is_active', 'country_name', 'image') + fields = ('id', 'name', 'dob', 'gender', 'phone_number', 'is_active', 'country_name', 'image') @staticmethod def get_name(obj): @@ -83,13 +98,22 @@ class GuardianSerializer(serializers.ModelSerializer): """ return f"{obj.user.first_name} {obj.user.last_name}" if obj.user.last_name else obj.user.first_name + @staticmethod + def get_phone_number(obj): + """ + :param obj: user object + :return: user phone number + """ + return f"+{obj.country_code}{obj.phone}" if obj.country_code and obj.phone else obj.phone + class JuniorSerializer(serializers.ModelSerializer): name = serializers.SerializerMethodField() + phone_number = serializers.SerializerMethodField() class Meta: model = Junior - fields = ('name', 'dob', 'gender', 'phone', 'is_active', 'country_name', 'image') + fields = ('id', 'name', 'dob', 'gender', 'phone_number', 'is_active', 'country_name', 'image') @staticmethod def get_name(obj): @@ -99,6 +123,14 @@ class JuniorSerializer(serializers.ModelSerializer): """ return f"{obj.auth.first_name} {obj.auth.last_name}" if obj.auth.last_name else obj.auth.first_name + @staticmethod + def get_phone_number(obj): + """ + :param obj: user object + :return: user phone number + """ + return f"+{obj.country_code}{obj.phone}" if obj.country_code and obj.phone else obj.phone + class UserManagementDetailSerializer(serializers.ModelSerializer): user_type = serializers.SerializerMethodField() @@ -111,7 +143,7 @@ class UserManagementDetailSerializer(serializers.ModelSerializer): meta class """ model = USER - fields = ('user_type', 'email', 'guardian_profile', 'junior_profile', 'associated_users') + fields = ('id', 'user_type', 'email', 'guardian_profile', 'junior_profile', 'associated_users') @staticmethod def get_user_type(obj): @@ -133,3 +165,7 @@ class UserManagementDetailSerializer(serializers.ModelSerializer): junior = Junior.objects.filter(guardian_code__contains=[profile.guardian_code], is_verified=True) serializer = JuniorSerializer(junior, many=True) return serializer.data + elif profile := obj.junior_profile.all().first(): + guardian = Guardian.objects.filter(guardian_code__in=profile.guardian_code, is_verified=True) + serializer = GuardianSerializer(guardian, many=True) + return serializer.data diff --git a/web_admin/views/article.py b/web_admin/views/article.py index b8f8a1f..314f36e 100644 --- a/web_admin/views/article.py +++ b/web_admin/views/article.py @@ -76,10 +76,11 @@ class ArticleViewSet(GenericViewSet, mixins.CreateModelMixin, mixins.UpdateModel :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) + return custom_response(None, data=serializer.data, count=count) def retrieve(self, request, *args, **kwargs): """ diff --git a/web_admin/views/user_management.py b/web_admin/views/user_management.py index ffca49d..54dc874 100644 --- a/web_admin/views/user_management.py +++ b/web_admin/views/user_management.py @@ -8,6 +8,7 @@ from rest_framework import status from rest_framework.decorators import action from rest_framework.permissions import IsAuthenticated, AllowAny from django.contrib.auth import get_user_model +from django.db.models import Q # local imports from account.utils import custom_response, custom_error_response @@ -27,16 +28,23 @@ class UserManagementViewSet(GenericViewSet, mixins.ListModelMixin, mixins.Retrie """ serializer_class = UserManagementListSerializer permission_classes = [] - queryset = USER.objects.filter(is_superuser=False).prefetch_related( - 'guardian_profile', 'junior_profile') + 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') + filter_backends = (SearchFilter,) + search_fields = ['first_name', 'last_name'] def get_queryset(self): if self.request.query_params.get('user_type') == dict(USER_TYPE).get('2'): - return self.queryset.filter(junior_profile__isnull=True) + queryset = self.queryset.filter(junior_profile__isnull=True) elif self.request.query_params.get('user_type') == dict(USER_TYPE).get('1'): - return self.queryset.filter(guardian_profile__isnull=True) + queryset = self.queryset.filter(guardian_profile__isnull=True) else: - return self.queryset + queryset = self.queryset + queryset = self.filter_queryset(queryset) + return queryset def list(self, request, *args, **kwargs): """ @@ -45,8 +53,11 @@ class UserManagementViewSet(GenericViewSet, mixins.ListModelMixin, mixins.Retrie :return: """ queryset = self.get_queryset() - serializer = self.serializer_class(queryset, many=True) - return custom_response(None, data=serializer.data) + 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) def retrieve(self, request, *args, **kwargs): queryset = self.queryset From 5f1c645e3a26627a7d683dd4d2f626289e022213 Mon Sep 17 00:00:00 2001 From: abutalib-kiwi Date: Fri, 4 Aug 2023 16:27:46 +0530 Subject: [PATCH 15/61] user list api --- .../serializers/user_management_serializer.py | 38 +++++++++---------- web_admin/views/article.py | 2 +- web_admin/views/user_management.py | 35 +++++++++++++++-- 3 files changed, 50 insertions(+), 25 deletions(-) diff --git a/web_admin/serializers/user_management_serializer.py b/web_admin/serializers/user_management_serializer.py index 5293cc4..78a1136 100644 --- a/web_admin/serializers/user_management_serializer.py +++ b/web_admin/serializers/user_management_serializer.py @@ -84,11 +84,18 @@ class UserManagementListSerializer(serializers.ModelSerializer): class GuardianSerializer(serializers.ModelSerializer): name = serializers.SerializerMethodField() - phone_number = serializers.SerializerMethodField() class Meta: model = Guardian - fields = ('id', 'name', 'dob', 'gender', 'phone_number', 'is_active', 'country_name', 'image') + fields = ('id', 'name', 'dob', 'gender', 'country_code', 'phone', 'is_active', 'country_name', 'image') + + def update(self, instance, validated_data): + instance.user.email = self.context.get('email', instance.user.email) + instance.user.save() + instance.country_code = validated_data.get('country_code', instance.country_code) + instance.phone = validated_data.get('phone', instance.phone) + instance.save() + return instance @staticmethod def get_name(obj): @@ -98,22 +105,21 @@ class GuardianSerializer(serializers.ModelSerializer): """ return f"{obj.user.first_name} {obj.user.last_name}" if obj.user.last_name else obj.user.first_name - @staticmethod - def get_phone_number(obj): - """ - :param obj: user object - :return: user phone number - """ - return f"+{obj.country_code}{obj.phone}" if obj.country_code and obj.phone else obj.phone - class JuniorSerializer(serializers.ModelSerializer): name = serializers.SerializerMethodField() - phone_number = serializers.SerializerMethodField() class Meta: model = Junior - fields = ('id', 'name', 'dob', 'gender', 'phone_number', 'is_active', 'country_name', 'image') + fields = ('id', 'name', 'dob', 'gender', 'country_code', 'phone', 'is_active', 'country_name', 'image') + + def update(self, instance, validated_data): + instance.auth.email = self.context.get('email', instance.auth.email) + instance.auth.save() + instance.country_code = validated_data.get('country_code', instance.country_code) + instance.phone = validated_data.get('phone', instance.phone) + instance.save() + return instance @staticmethod def get_name(obj): @@ -123,14 +129,6 @@ class JuniorSerializer(serializers.ModelSerializer): """ return f"{obj.auth.first_name} {obj.auth.last_name}" if obj.auth.last_name else obj.auth.first_name - @staticmethod - def get_phone_number(obj): - """ - :param obj: user object - :return: user phone number - """ - return f"+{obj.country_code}{obj.phone}" if obj.country_code and obj.phone else obj.phone - class UserManagementDetailSerializer(serializers.ModelSerializer): user_type = serializers.SerializerMethodField() diff --git a/web_admin/views/article.py b/web_admin/views/article.py index 314f36e..be93b4c 100644 --- a/web_admin/views/article.py +++ b/web_admin/views/article.py @@ -29,7 +29,7 @@ class ArticleViewSet(GenericViewSet, mixins.CreateModelMixin, mixins.UpdateModel serializer_class = ArticleSerializer permission_classes = [IsAuthenticated, AdminPermission] queryset = Article - filter_backends = (OrderingFilter, SearchFilter,) + filter_backends = (SearchFilter,) search_fields = ['title'] http_method_names = ['get', 'post', 'put', 'delete'] diff --git a/web_admin/views/user_management.py b/web_admin/views/user_management.py index 54dc874..db41855 100644 --- a/web_admin/views/user_management.py +++ b/web_admin/views/user_management.py @@ -14,15 +14,16 @@ from django.db.models import Q from account.utils import custom_response, custom_error_response from base.constants import USER_TYPE 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.user_management_serializer import (UserManagementListSerializer, - UserManagementDetailSerializer) + UserManagementDetailSerializer, GuardianSerializer, + JuniorSerializer) USER = get_user_model() -class UserManagementViewSet(GenericViewSet, mixins.ListModelMixin, mixins.RetrieveModelMixin): +class UserManagementViewSet(GenericViewSet, mixins.ListModelMixin, + mixins.RetrieveModelMixin, mixins.UpdateModelMixin): """ api to manage (list, view, edit) user """ @@ -49,7 +50,7 @@ class UserManagementViewSet(GenericViewSet, mixins.ListModelMixin, mixins.Retrie def list(self, request, *args, **kwargs): """ api method to list all the user - :param request: + :param request: user_type {'guardian' for Guardian list, 'junior' for Junior list} :return: """ queryset = self.get_queryset() @@ -60,6 +61,14 @@ class UserManagementViewSet(GenericViewSet, mixins.ListModelMixin, mixins.Retrie return custom_response(None, data=serializer.data, count=count) def retrieve(self, request, *args, **kwargs): + """ + to get details of single user + :param request: user_id along with + user_type {'guardian' for Guardian, 'junior' for Junior} mandatory + :return: user details + """ + if self.request.query_params.get('user_type') not in [dict(USER_TYPE).get('1'), dict(USER_TYPE).get('2')]: + return custom_error_response('Action not allowed', 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']) @@ -67,3 +76,21 @@ class UserManagementViewSet(GenericViewSet, mixins.ListModelMixin, mixins.Retrie queryset = queryset.filter(junior_profile__auth__id=kwargs['pk']) serializer = UserManagementDetailSerializer(queryset, many=True) return custom_response(None, data=serializer.data) + + def partial_update(self, request, *args, **kwargs): + if self.request.query_params.get('user_type') not in [dict(USER_TYPE).get('1'), dict(USER_TYPE).get('2')]: + return custom_error_response('Action not allowed', 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']).first() + serializer = GuardianSerializer(queryset.guardian_profile.all().first(), + request.data, context={'email': request.data.get('email', queryset.email)}) + + elif self.request.query_params.get('user_type') == dict(USER_TYPE).get('1'): + queryset = queryset.filter(junior_profile__auth__id=kwargs['pk']).first() + serializer = JuniorSerializer(queryset.junior_profile.all().first(), + request.data, context={'email': request.data.get('email', queryset.email)}) + + serializer.is_valid(raise_exception=True) + serializer.save() + return custom_response('Profile updated successfully.') From baacb1a18fac585be1e67b6e3743792c53720eb9 Mon Sep 17 00:00:00 2001 From: abutalib-kiwi Date: Fri, 4 Aug 2023 16:28:58 +0530 Subject: [PATCH 16/61] user edit api --- .../serializers/user_management_serializer.py | 24 +++++++++++++++---- web_admin/views/user_management.py | 2 +- 2 files changed, 20 insertions(+), 6 deletions(-) diff --git a/web_admin/serializers/user_management_serializer.py b/web_admin/serializers/user_management_serializer.py index 78a1136..5221e12 100644 --- a/web_admin/serializers/user_management_serializer.py +++ b/web_admin/serializers/user_management_serializer.py @@ -23,7 +23,8 @@ class UserManagementListSerializer(serializers.ModelSerializer): user management serializer """ name = serializers.SerializerMethodField() - phone_number = serializers.SerializerMethodField() + country_code = serializers.SerializerMethodField() + phone = serializers.SerializerMethodField() user_type = serializers.SerializerMethodField() is_active = serializers.SerializerMethodField() @@ -32,7 +33,7 @@ class UserManagementListSerializer(serializers.ModelSerializer): meta class """ model = USER - fields = ('id', 'name', 'email', 'phone_number', 'user_type', 'is_active') + fields = ('id', 'name', 'email', 'country_code', 'phone', 'user_type', 'is_active') @staticmethod def get_name(obj): @@ -43,15 +44,28 @@ class UserManagementListSerializer(serializers.ModelSerializer): return f"{obj.first_name} {obj.last_name}" if obj.last_name else obj.first_name @staticmethod - def get_phone_number(obj): + def get_country_code(obj): """ :param obj: user object :return: user phone number """ if profile := obj.guardian_profile.all().first(): - return f"+{profile.country_code}{profile.phone}" if profile.country_code and profile.phone else profile.phone + return profile.country_code if profile.country_code else None elif profile := obj.junior_profile.all().first(): - return f"+{profile.country_code}{profile.phone}" if profile.country_code and profile.phone else profile.phone + return profile.country_code if profile.country_code else None + else: + return None + + @staticmethod + def get_phone(obj): + """ + :param obj: user object + :return: user phone number + """ + if profile := obj.guardian_profile.all().first(): + return profile.phone if profile.phone else None + elif profile := obj.junior_profile.all().first(): + return profile.phone if profile.phone else None else: return None diff --git a/web_admin/views/user_management.py b/web_admin/views/user_management.py index db41855..e4018ac 100644 --- a/web_admin/views/user_management.py +++ b/web_admin/views/user_management.py @@ -28,7 +28,7 @@ class UserManagementViewSet(GenericViewSet, mixins.ListModelMixin, api to manage (list, view, edit) user """ serializer_class = UserManagementListSerializer - permission_classes = [] + permission_classes = [IsAuthenticated, AdminPermission] queryset = USER.objects.filter(is_superuser=False ).prefetch_related('guardian_profile', 'junior_profile' From 4a2f36cde801a3fba3dd6154bca884c4de195368 Mon Sep 17 00:00:00 2001 From: abutalib-kiwi Date: Mon, 7 Aug 2023 10:55:34 +0530 Subject: [PATCH 17/61] edit user validations, active-inactive api --- base/messages.py | 4 +- .../serializers/user_management_serializer.py | 77 ++++++++++++++++--- web_admin/views/article.py | 4 +- web_admin/views/user_management.py | 45 +++++++++-- 4 files changed, 108 insertions(+), 22 deletions(-) diff --git a/base/messages.py b/base/messages.py index bff508d..e891ae1 100644 --- a/base/messages.py +++ b/base/messages.py @@ -142,7 +142,9 @@ SUCCESS_CODE = { "3033": "Valid Referral code", "3034": "Invite guardian successfully", "3035": "Task started successfully", - "3036": "Task reassign successfully" + "3036": "Task reassign successfully", + "3037": "Profile has been updated successfully.", + "3038": "Status has been changed successfully." } """status code error""" STATUS_CODE_ERROR = { diff --git a/web_admin/serializers/user_management_serializer.py b/web_admin/serializers/user_management_serializer.py index 5221e12..9333155 100644 --- a/web_admin/serializers/user_management_serializer.py +++ b/web_admin/serializers/user_management_serializer.py @@ -5,15 +5,11 @@ web_admin user_management serializers file from rest_framework import serializers from django.contrib.auth import get_user_model -from base.constants import (ARTICLE_SURVEY_POINTS, MAX_ARTICLE_CARD, MIN_ARTICLE_SURVEY, MAX_ARTICLE_SURVEY, NUMBER, - USER_TYPE, ARTICLE_CARD_IMAGE_FOLDER) +from base.constants import USER_TYPE # local imports -from base.messages import ERROR_CODE +from base.messages import ERROR_CODE, SUCCESS_CODE from guardian.models import Guardian -from guardian.utils import upload_image_to_alibaba from junior.models import Junior -from web_admin.models import Article, ArticleCard, SurveyOption, ArticleSurvey, DefaultArticleCardImage -from web_admin.utils import pop_id, get_image_url USER = get_user_model() @@ -97,14 +93,41 @@ class UserManagementListSerializer(serializers.ModelSerializer): class GuardianSerializer(serializers.ModelSerializer): + """ + guardian serializer + """ name = serializers.SerializerMethodField() + email = serializers.CharField(required=False) class Meta: + """ + meta class + """ model = Guardian - fields = ('id', 'name', 'dob', 'gender', 'country_code', 'phone', 'is_active', 'country_name', 'image') + fields = ('id', 'name', 'dob', 'gender', 'country_code', 'phone', 'is_active', 'country_name', 'image', 'email') + + def validate(self, attrs): + """ + to validate request data + :return: validated attrs + """ + email = attrs.get('email') + phone = attrs.get('phone') + if USER.objects.filter(email=email).exclude(id=self.context.get('user_id')).exists(): + raise serializers.ValidationError({'details': ERROR_CODE['2003']}) + if Guardian.objects.filter(phone=phone).exclude(user__id=self.context.get('user_id')).exists(): + raise serializers.ValidationError({'details': ERROR_CODE['2012']}) + return attrs def update(self, instance, validated_data): - instance.user.email = self.context.get('email', instance.user.email) + """ + to update user and its related profile + :param instance: user's guardian object + :param validated_data: + :return: guardian object + """ + instance.user.email = self.validated_data.get('email', instance.user.email) + instance.user.username = self.validated_data.get('email', instance.user.username) instance.user.save() instance.country_code = validated_data.get('country_code', instance.country_code) instance.phone = validated_data.get('phone', instance.phone) @@ -121,14 +144,40 @@ class GuardianSerializer(serializers.ModelSerializer): class JuniorSerializer(serializers.ModelSerializer): + """ + junior serializer + """ name = serializers.SerializerMethodField() + email = serializers.CharField(required=False) class Meta: + """ + meta class + """ model = Junior - fields = ('id', 'name', 'dob', 'gender', 'country_code', 'phone', 'is_active', 'country_name', 'image') + fields = ('id', 'name', 'dob', 'gender', 'country_code', 'phone', 'is_active', 'country_name', 'image', 'email') + + def validate(self, attrs): + """ + to validate request data + :return: validated attrs + """ + email = attrs.get('email') + phone = attrs.get('phone') + if email and USER.objects.filter(email=email).exclude(id=self.context.get('user_id')).exists(): + raise serializers.ValidationError({'details': ERROR_CODE['2003']}) + if phone and Junior.objects.filter(phone=phone).exclude(auth__id=self.context.get('user_id')).exists(): + raise serializers.ValidationError({'details': ERROR_CODE['2012']}) + return attrs def update(self, instance, validated_data): - instance.auth.email = self.context.get('email', instance.auth.email) + """ + :param instance: user's junior object + :param validated_data: validated data + :return: instance + """ + 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.country_code = validated_data.get('country_code', instance.country_code) instance.phone = validated_data.get('phone', instance.phone) @@ -145,6 +194,9 @@ class JuniorSerializer(serializers.ModelSerializer): class UserManagementDetailSerializer(serializers.ModelSerializer): + """ + user management details serializer + """ user_type = serializers.SerializerMethodField() guardian_profile = GuardianSerializer(many=True) junior_profile = JuniorSerializer(many=True) @@ -163,7 +215,6 @@ class UserManagementDetailSerializer(serializers.ModelSerializer): :param obj: user object :return: user type """ - profile = obj.guardian_profile.all().first() if obj.guardian_profile.all().first(): return dict(USER_TYPE).get('2') elif obj.junior_profile.all().first(): @@ -173,6 +224,10 @@ class UserManagementDetailSerializer(serializers.ModelSerializer): @staticmethod def get_associated_users(obj): + """ + :param obj: user object + :return: associated user + """ if profile := obj.guardian_profile.all().first(): junior = Junior.objects.filter(guardian_code__contains=[profile.guardian_code], is_verified=True) serializer = JuniorSerializer(junior, many=True) diff --git a/web_admin/views/article.py b/web_admin/views/article.py index be93b4c..53da6e1 100644 --- a/web_admin/views/article.py +++ b/web_admin/views/article.py @@ -109,7 +109,7 @@ 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=['get'], url_name='remove_card', url_path='remove_card', + @action(methods=['get'], url_name='remove-card', url_path='remove-card', detail=True) def remove_article_card(self, request, *args, **kwargs): """ @@ -125,7 +125,7 @@ class ArticleViewSet(GenericViewSet, mixins.CreateModelMixin, mixins.UpdateModel except AttributeError: return custom_error_response(ERROR_CODE["2042"], response_status=status.HTTP_400_BAD_REQUEST) - @action(methods=['get'], url_name='remove_survey', url_path='remove_survey', + @action(methods=['get'], url_name='remove-survey', url_path='remove-survey', detail=True) def remove_article_survey(self, request, *args, **kwargs): """ diff --git a/web_admin/views/user_management.py b/web_admin/views/user_management.py index e4018ac..256248b 100644 --- a/web_admin/views/user_management.py +++ b/web_admin/views/user_management.py @@ -13,7 +13,7 @@ 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.messages import SUCCESS_CODE, ERROR_CODE +from base.messages import SUCCESS_CODE from web_admin.permission import AdminPermission from web_admin.serializers.user_management_serializer import (UserManagementListSerializer, UserManagementDetailSerializer, GuardianSerializer, @@ -78,19 +78,48 @@ class UserManagementViewSet(GenericViewSet, mixins.ListModelMixin, return custom_response(None, data=serializer.data) def partial_update(self, request, *args, **kwargs): + """ + api method to update user detail (email, phone number) + :param request: user_id along with + 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')]: return custom_error_response('Action not allowed', 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']).first() - serializer = GuardianSerializer(queryset.guardian_profile.all().first(), - request.data, context={'email': request.data.get('email', queryset.email)}) + user_obj = queryset.filter(guardian_profile__user__id=kwargs['pk']).first() + serializer = GuardianSerializer(user_obj.guardian_profile.all().first(), + request.data, context={'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']).first() - serializer = JuniorSerializer(queryset.junior_profile.all().first(), - request.data, context={'email': request.data.get('email', queryset.email)}) + user_obj = queryset.filter(junior_profile__auth__id=kwargs['pk']).first() + serializer = JuniorSerializer(user_obj.junior_profile.all().first(), + request.data, context={'user_id': kwargs['pk']}) serializer.is_valid(raise_exception=True) serializer.save() - return custom_response('Profile updated successfully.') + return custom_response(SUCCESS_CODE['3037']) + + @action(methods=['get'], url_name='change-status', url_path='change-status', detail=True) + def change_status(self, request, *args, **kwargs): + """ + api to change user status (mark as active or inactive) + :param request: user_id along with + 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')]: + return custom_error_response('Action not allowed', 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() + return custom_response(SUCCESS_CODE['3038']) From e85334691064be94beb4f9b5af72b6e436b7d47a Mon Sep 17 00:00:00 2001 From: jain Date: Mon, 7 Aug 2023 11:26:23 +0530 Subject: [PATCH 18/61] notifications API --- base/messages.py | 4 +++- guardian/urls.py | 5 +++- guardian/views.py | 30 +++++++++++++++++++++++ notifications/serializers.py | 21 +++++++++++++++- notifications/urls.py | 3 ++- notifications/views.py | 46 +++++++++++++++++++++++++++++++----- 6 files changed, 99 insertions(+), 10 deletions(-) diff --git a/base/messages.py b/base/messages.py index bff508d..9f36be6 100644 --- a/base/messages.py +++ b/base/messages.py @@ -142,7 +142,9 @@ SUCCESS_CODE = { "3033": "Valid Referral code", "3034": "Invite guardian successfully", "3035": "Task started successfully", - "3036": "Task reassign successfully" + "3036": "Task reassign successfully", + # notification read + "3037": "Notification read successfully", } """status code error""" STATUS_CODE_ERROR = { diff --git a/guardian/urls.py b/guardian/urls.py index b14aa77..e95ea8e 100644 --- a/guardian/urls.py +++ b/guardian/urls.py @@ -2,7 +2,8 @@ """Django import""" from django.urls import path, include from .views import (SignupViewset, UpdateGuardianProfile, AllTaskListAPIView, CreateTaskAPIView, TaskListAPIView, - SearchTaskListAPIView, TopJuniorListAPIView, ApproveJuniorAPIView, ApproveTaskAPIView) + SearchTaskListAPIView, TopJuniorListAPIView, ApproveJuniorAPIView, ApproveTaskAPIView, + GuardianListAPIView) """Third party import""" from rest_framework import routers @@ -36,6 +37,8 @@ router.register('filter-task', SearchTaskListAPIView, basename='filter-task') router.register('approve-junior', ApproveJuniorAPIView, basename='approve-junior') # Approve junior API""" router.register('approve-task', ApproveTaskAPIView, basename='approve-task') +# guardian list API""" +router.register('guardian-list', GuardianListAPIView, basename='guardian-list') # Define Url pattern""" urlpatterns = [ path('api/v1/', include(router.urls)), diff --git a/guardian/views.py b/guardian/views.py index 15b29c5..47fbdda 100644 --- a/guardian/views.py +++ b/guardian/views.py @@ -328,3 +328,33 @@ class ApproveTaskAPIView(viewsets.ViewSet): except Exception as e: return custom_error_response(str(e), response_status=status.HTTP_400_BAD_REQUEST) +class GuardianListAPIView(viewsets.ModelViewSet): + """Junior list of assosicated guardian""" + pass + + # serializer_class = JuniorDetailListSerializer + # queryset = Junior.objects.all() + # permission_classes = [IsAuthenticated] + # filter_backends = (SearchFilter,) + # search_fields = ['auth__first_name', 'auth__last_name'] + # http_method_names = ('get',) + # + # def get_queryset(self): + # queryset = self.filter_queryset(self.queryset) + # return queryset + # + # def list(self, request, *args, **kwargs): + # """ junior list""" + # try: + # update_positions_based_on_points() + # 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) + # 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: + # return custom_error_response(str(e), response_status=status.HTTP_400_BAD_REQUEST) diff --git a/notifications/serializers.py b/notifications/serializers.py index 053096f..a085cf6 100644 --- a/notifications/serializers.py +++ b/notifications/serializers.py @@ -6,7 +6,7 @@ from rest_framework import serializers # local imports from notifications.utils import register_fcm_token - +from notifications.models import Notification class RegisterDevice(serializers.Serializer): """ @@ -26,3 +26,22 @@ class RegisterDevice(serializers.Serializer): device_type = validated_data['type'] return register_fcm_token(self.context['user_id'], registration_id, validated_data['device_id'], device_type) + +class NotificationListSerailizer(serializers.ModelSerializer): + """List of notification""" + + class Meta(object): + """meta info""" + model = Notification + fields = ['id', 'data', 'is_read'] + +class ReadNotificationSerializer(serializers.ModelSerializer): + """User task Serializer""" + class Meta(object): + """Meta class""" + model = Notification + fields = ('id',) + def update(self, instance, validated_data): + instance.is_read = True + instance.save() + return instance diff --git a/notifications/urls.py b/notifications/urls.py index b184d02..713aae3 100644 --- a/notifications/urls.py +++ b/notifications/urls.py @@ -6,7 +6,7 @@ from django.urls import path, include from rest_framework import routers # local imports -from notifications.views import NotificationViewSet +from notifications.views import NotificationViewSet, ReadNotification # initiate router router = routers.SimpleRouter() @@ -15,4 +15,5 @@ router.register('notifications', NotificationViewSet, basename='notifications') urlpatterns = [ path('api/v1/', include(router.urls)), + path('api/v1/read-notification/', ReadNotification.as_view()), ] diff --git a/notifications/views.py b/notifications/views.py index a8659e3..d4dec80 100644 --- a/notifications/views.py +++ b/notifications/views.py @@ -3,18 +3,17 @@ notifications views file """ # django imports from django.db.models import Q -from rest_framework import viewsets from rest_framework.decorators import action from rest_framework.permissions import IsAuthenticated from rest_framework.response import Response - +from rest_framework import viewsets, status, views # local imports -from account.utils import custom_response -from base.messages import SUCCESS_CODE +from account.utils import custom_response, custom_error_response +from base.messages import SUCCESS_CODE, ERROR_CODE from notifications.constants import TEST_NOTIFICATION -from notifications.serializers import RegisterDevice +from notifications.serializers import RegisterDevice, NotificationListSerailizer, ReadNotificationSerializer from notifications.utils import send_notification - +from notifications.models import Notification class NotificationViewSet(viewsets.GenericViewSet): """ used to do the notification actions """ @@ -40,3 +39,38 @@ class NotificationViewSet(viewsets.GenericViewSet): """ send_notification.delay(TEST_NOTIFICATION, 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=NotificationListSerailizer) + def notification_list(self, request): + """ + notification list + """ + try: + queryset = Notification.objects.filter(notification_to=request.user) + serializer = NotificationListSerailizer(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 = str(self.request.data.get('notification_id')) + notification_queryset = Notification.objects.filter(id=notification_id, notification_to=self.request.user).last() + if notification_queryset: + # use ReadNotificationSerializer serializer + serializer = ReadNotificationSerializer(notification_queryset, data=request.data, partial=True) + if serializer.is_valid(): + # save serializer + serializer.save() + return custom_response(SUCCESS_CODE['3037'], 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) From 83ec922584df163b05f523983be2e5027f1a3310 Mon Sep 17 00:00:00 2001 From: abutalib-kiwi Date: Mon, 7 Aug 2023 12:00:58 +0530 Subject: [PATCH 19/61] admin login api modified --- account/serializers.py | 31 ++++++++++++++++++++++++++ account/urls.py | 5 +++-- account/views.py | 35 ++++++++++++++++++++++++++---- base/messages.py | 3 ++- web_admin/views/user_management.py | 7 +++--- 5 files changed, 71 insertions(+), 10 deletions(-) diff --git a/account/serializers.py b/account/serializers.py index d4fd335..284ae09 100644 --- a/account/serializers.py +++ b/account/serializers.py @@ -137,6 +137,37 @@ class ForgotPasswordSerializer(serializers.Serializer): """Forget password serializer""" email = serializers.EmailField() + +class AdminLoginSerializer(serializers.ModelSerializer): + """admin login serializer""" + email = serializers.EmailField(required=True) + password = serializers.CharField(required=True) + + class Meta: + """ + meta class + """ + model = User + fields = ('email', 'password') + + 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() + + if not user: + raise serializers.ValidationError({'details': ERROR_CODE['2063']}) + elif not user.check_password(attrs['password']): + raise serializers.ValidationError({'details': ERROR_CODE['2031']}) + self.context.update({'user': user}) + return attrs + + def create(self, validated_data): + """ + used to return the user object after validation + """ + return self.context['user'] + + class SuperUserSerializer(serializers.ModelSerializer): """Super admin serializer""" user_type = serializers.SerializerMethodField('get_user_type') diff --git a/account/urls.py b/account/urls.py index ef3d026..02ac124 100644 --- a/account/urls.py +++ b/account/urls.py @@ -28,14 +28,15 @@ from .views import (UserLogin, SendPhoneOtp, UserPhoneVerification, UserEmailVer ForgotPasswordAPIView, ResetPasswordAPIView, ChangePasswordAPIView, UpdateProfileImage, GoogleLoginViewSet, SigninWithApple, ProfileAPIViewSet, UploadImageAPIViewSet, DefaultImageAPIViewSet, DeleteUserProfileAPIViewSet, UserNotificationAPIViewSet, - UpdateUserNotificationAPIViewSet, SendSupportEmail, LogoutAPIView, AccessTokenAPIView) + UpdateUserNotificationAPIViewSet, SendSupportEmail, LogoutAPIView, AccessTokenAPIView, + AdminLoginViewSet) """Router""" router = routers.SimpleRouter() """API End points with router""" router.register('user', UserLogin, basename='user') """super admin login""" -router.register('admin', UserLogin, basename='admin') +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') diff --git a/account/views.py b/account/views.py index c95aa7e..61c4e0e 100644 --- a/account/views.py +++ b/account/views.py @@ -1,7 +1,7 @@ """Account view """ from notifications.utils import remove_fcm_token -"""Django import""" +# django imports from datetime import datetime, timedelta from rest_framework import viewsets, status, views from rest_framework.decorators import action @@ -18,19 +18,21 @@ import google.auth.transport.requests from rest_framework import status import requests from rest_framework.response import Response +from rest_framework import mixins from django.conf import settings -"""App Import""" + +# local imports from guardian.models import Guardian from junior.models import Junior from guardian.utils import upload_image_to_alibaba from account.models import UserDeviceDetails, UserPhoneOtp, UserEmailOtp, DefaultTaskImages, UserNotification from django.contrib.auth.models import User -"""Account serializer""" from .serializers import (SuperUserSerializer, GuardianSerializer, JuniorSerializer, EmailVerificationSerializer, ForgotPasswordSerializer, ResetPasswordSerializer, ChangePasswordSerializer, GoogleLoginSerializer, UpdateGuardianImageSerializer, UpdateJuniorProfileImageSerializer, DefaultTaskImagesSerializer, DefaultTaskImagesDetailsSerializer, UserDeleteSerializer, - UserNotificationSerializer, UpdateUserNotificationSerializer, UserPhoneOtpSerializer) + UserNotificationSerializer, UpdateUserNotificationSerializer, UserPhoneOtpSerializer, + AdminLoginSerializer) from rest_framework_simplejwt.tokens import RefreshToken from base.messages import ERROR_CODE, SUCCESS_CODE from base.constants import NUMBER, ZOD, JUN, GRD @@ -346,6 +348,31 @@ class UserLogin(viewsets.ViewSet): data = {"auth_token": access_token, "refresh_token":refresh_token, "user_type": '3'} return custom_response(None, data, response_status=status.HTTP_200_OK) + +class AdminLoginViewSet(viewsets.GenericViewSet): + """ + admin login api + """ + serializer_class = AdminLoginSerializer + + @action(methods=['post'], url_name='login', url_path='login', detail=False) + def admin_login(self, request, *args, **kwargs): + """ + :param request: + :return: + """ + serializer = self.serializer_class(data=request.data) + serializer.is_valid(raise_exception=True) + user = serializer.save() + refresh = RefreshToken.for_user(user) + access_token = str(refresh.access_token) + refresh_token = str(refresh) + data = {"auth_token": access_token, "refresh_token": refresh_token, "username": user.username, + "email": user.email, "first_name": user.first_name, "last_name": user.last_name, + "is_active": user.is_active, "user_type": '3', "is_superuser": user.is_superuser} + return custom_response(None, data) + + class UserEmailVerification(viewsets.ModelViewSet): """User Email verification""" serializer_class = EmailVerificationSerializer diff --git a/base/messages.py b/base/messages.py index e891ae1..2b57e4e 100644 --- a/base/messages.py +++ b/base/messages.py @@ -92,7 +92,8 @@ ERROR_CODE = { "2063": "Unauthorized access.", "2064": "To change your password first request an OTP and get it verify then change your password.", "2065": "Passwords do not match. Please try again.", - "2066": "Task does not exist or not in expired state" + "2066": "Task does not exist or not in expired state", + "2067": "Action not allowed. User type missing." } """Success message code""" SUCCESS_CODE = { diff --git a/web_admin/views/user_management.py b/web_admin/views/user_management.py index 256248b..20184cd 100644 --- a/web_admin/views/user_management.py +++ b/web_admin/views/user_management.py @@ -13,7 +13,7 @@ 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.messages import SUCCESS_CODE +from base.messages import SUCCESS_CODE, ERROR_CODE from web_admin.permission import AdminPermission from web_admin.serializers.user_management_serializer import (UserManagementListSerializer, UserManagementDetailSerializer, GuardianSerializer, @@ -36,6 +36,7 @@ class UserManagementViewSet(GenericViewSet, mixins.ListModelMixin, guardian_profile__isnull=True).order_by('date_joined') filter_backends = (SearchFilter,) search_fields = ['first_name', 'last_name'] + http_method_names = ['get', 'post', 'patch'] def get_queryset(self): if self.request.query_params.get('user_type') == dict(USER_TYPE).get('2'): @@ -85,7 +86,7 @@ class UserManagementViewSet(GenericViewSet, mixins.ListModelMixin, :return: success message """ if self.request.query_params.get('user_type') not in [dict(USER_TYPE).get('1'), dict(USER_TYPE).get('2')]: - return custom_error_response('Action not allowed', status.HTTP_400_BAD_REQUEST) + 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() @@ -110,7 +111,7 @@ class UserManagementViewSet(GenericViewSet, mixins.ListModelMixin, :return: success message """ if self.request.query_params.get('user_type') not in [dict(USER_TYPE).get('1'), dict(USER_TYPE).get('2')]: - return custom_error_response('Action not allowed', status.HTTP_400_BAD_REQUEST) + 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() From f377e283fd55e4026cb568d8fbe82dd4add5cea9 Mon Sep 17 00:00:00 2001 From: jain Date: Mon, 7 Aug 2023 13:25:01 +0530 Subject: [PATCH 20/61] guardian list api --- base/messages.py | 3 ++- guardian/serializers.py | 35 ++++++++++++++++++++++++++++- guardian/views.py | 50 +++++++++++++++++------------------------ junior/serializers.py | 6 +++-- junior/views.py | 5 +++-- 5 files changed, 63 insertions(+), 36 deletions(-) diff --git a/base/messages.py b/base/messages.py index 979d970..470c674 100644 --- a/base/messages.py +++ b/base/messages.py @@ -92,7 +92,8 @@ ERROR_CODE = { "2063": "Unauthorized access.", "2064": "To change your password first request an OTP and get it verify then change your password.", "2065": "Passwords do not match. Please try again.", - "2066": "Task does not exist or not in expired state" + "2066": "Task does not exist or not in expired state", + "2067": "No guardian associated with this junior" } """Success message code""" SUCCESS_CODE = { diff --git a/guardian/serializers.py b/guardian/serializers.py index db465ad..04acb9d 100644 --- a/guardian/serializers.py +++ b/guardian/serializers.py @@ -23,7 +23,7 @@ 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 junior.models import Junior, JuniorPoints +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 @@ -386,3 +386,36 @@ class ApproveTaskSerializer(serializers.ModelSerializer): junior_data.save() return junior_details +class GuardianDetailListSerializer(serializers.ModelSerializer): + """Guardian serializer""" + + first_name = serializers.SerializerMethodField('get_first_name') + last_name = serializers.SerializerMethodField('get_last_name') + email = serializers.SerializerMethodField('get_email') + image = serializers.SerializerMethodField('get_image') + guardian_id = serializers.SerializerMethodField('get_guardian_id') + + class Meta(object): + """Meta info""" + model = JuniorGuardianRelationship + fields = ['guardian_id', 'first_name', 'last_name', 'email', 'relationship', 'image', 'created_at', + 'updated_at'] + + def get_guardian_id(self,obj): + """first name of guardian""" + return obj.guardian.id + def get_first_name(self,obj): + """first name of guardian""" + return obj.guardian.user.first_name + + def get_last_name(self,obj): + """last name of guardian""" + return obj.guardian.user.last_name + + def get_email(self,obj): + """emailof guardian""" + return obj.guardian.user.email + + def get_image(self,obj): + """first name of guardian""" + return obj.guardian.image diff --git a/guardian/views.py b/guardian/views.py index 47fbdda..211d6ed 100644 --- a/guardian/views.py +++ b/guardian/views.py @@ -26,9 +26,10 @@ from django.utils import timezone # Import notification constant # Import send_notification function from .serializers import (UserSerializer, CreateGuardianSerializer, TaskSerializer, TaskDetailsSerializer, - TopJuniorSerializer, ApproveJuniorSerializer, ApproveTaskSerializer) + TopJuniorSerializer, ApproveJuniorSerializer, ApproveTaskSerializer, + GuardianDetailListSerializer) from .models import Guardian, JuniorTask -from junior.models import Junior, JuniorPoints +from junior.models import Junior, JuniorPoints, JuniorGuardianRelationship from account.models import UserEmailOtp, UserNotification from .tasks import generate_otp from account.utils import custom_response, custom_error_response, OTP_EXPIRY, send_otp_email @@ -329,32 +330,21 @@ class ApproveTaskAPIView(viewsets.ViewSet): return custom_error_response(str(e), response_status=status.HTTP_400_BAD_REQUEST) class GuardianListAPIView(viewsets.ModelViewSet): - """Junior list of assosicated guardian""" - pass + """Guardian list of assosicated junior""" - # serializer_class = JuniorDetailListSerializer - # queryset = Junior.objects.all() - # permission_classes = [IsAuthenticated] - # filter_backends = (SearchFilter,) - # search_fields = ['auth__first_name', 'auth__last_name'] - # http_method_names = ('get',) - # - # def get_queryset(self): - # queryset = self.filter_queryset(self.queryset) - # return queryset - # - # def list(self, request, *args, **kwargs): - # """ junior list""" - # try: - # update_positions_based_on_points() - # 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) - # 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: - # return custom_error_response(str(e), response_status=status.HTTP_400_BAD_REQUEST) + serializer_class = GuardianDetailListSerializer + permission_classes = [IsAuthenticated] + http_method_names = ('get',) + + def list(self, request, *args, **kwargs): + """ junior list""" + try: + guardian_data = JuniorGuardianRelationship.objects.filter(junior__auth__email=self.request.user) + # fetch junior object + if guardian_data: + # 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['2067'], response_status=status.HTTP_200_OK) + except Exception as e: + return custom_error_response(str(e), response_status=status.HTTP_400_BAD_REQUEST) diff --git a/junior/serializers.py b/junior/serializers.py index 316c1bf..1dc04da 100644 --- a/junior/serializers.py +++ b/junior/serializers.py @@ -89,7 +89,10 @@ class CreateJuniorSerializer(serializers.ModelSerializer): """condition for guardian code""" if guardian_code: junior.guardian_code = guardian_code - junior.dob = validated_data.get('dob',junior.dob) + guardian_data = Guardian.objects.filter(guardian_code=guardian_code[0]).last() + if guardian_data: + JuniorGuardianRelationship.objects.get_or_create(guardian=guardian_data, junior=junior) + 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) """Update country code and phone number""" @@ -289,7 +292,6 @@ class AddJuniorSerializer(serializers.ModelSerializer): UserNotification.objects.get_or_create(user=user_data) """Notification email""" junior_notification_email(email, full_name, email, password) - junior_approval_mail(guardian, full_name) # push notification send_notification.delay(SKIPPED_PROFILE_SETUP, None, junior_data.auth.id, {}) return junior_data diff --git a/junior/views.py b/junior/views.py index 3f3cd5a..15cd387 100644 --- a/junior/views.py +++ b/junior/views.py @@ -27,7 +27,7 @@ import requests # Import upload_image_to_alibaba # Import custom_response, custom_error_response # Import constants -from junior.models import Junior, JuniorPoints +from junior.models import Junior, JuniorPoints, JuniorGuardianRelationship from .serializers import (CreateJuniorSerializer, JuniorDetailListSerializer, AddJuniorSerializer,\ RemoveJuniorSerializer, CompleteTaskSerializer, JuniorPointsSerializer, AddGuardianSerializer, StartTaskSerializer, ReAssignTaskSerializer) @@ -144,7 +144,6 @@ class JuniorListAPIView(viewsets.ModelViewSet): class AddJuniorAPIView(viewsets.ModelViewSet): """Add Junior by guardian""" - queryset = Junior.objects.all() serializer_class = AddJuniorSerializer permission_classes = [IsAuthenticated] http_method_names = ('post',) @@ -172,6 +171,8 @@ class AddJuniorAPIView(viewsets.ModelViewSet): guardian = Guardian.objects.filter(user=self.request.user).first() junior.guardian_code = [guardian.guardian_code] junior.save() + JuniorGuardianRelationship.objects.get_or_create(guardian=guardian, junior=junior, + relationship=str(self.request.data['relationship'])) return True From b3b499e661a73b45cc97c22382cff36efab0eb83 Mon Sep 17 00:00:00 2001 From: jain Date: Mon, 7 Aug 2023 13:41:33 +0530 Subject: [PATCH 21/61] guardian list api --- base/messages.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/base/messages.py b/base/messages.py index 470c674..9d20dfb 100644 --- a/base/messages.py +++ b/base/messages.py @@ -93,7 +93,7 @@ ERROR_CODE = { "2064": "To change your password first request an OTP and get it verify then change your password.", "2065": "Passwords do not match. Please try again.", "2066": "Task does not exist or not in expired state", - "2067": "No guardian associated with this junior" + "2068": "No guardian associated with this junior" } """Success message code""" SUCCESS_CODE = { From c20249730a0513ae1b5b6eff6ad9d751607b6b71 Mon Sep 17 00:00:00 2001 From: jain Date: Mon, 7 Aug 2023 13:42:47 +0530 Subject: [PATCH 22/61] guardian list api --- guardian/views.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/guardian/views.py b/guardian/views.py index 211d6ed..1fa4e1b 100644 --- a/guardian/views.py +++ b/guardian/views.py @@ -345,6 +345,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['2067'], response_status=status.HTTP_200_OK) + return custom_error_response(ERROR_CODE['2068'], response_status=status.HTTP_200_OK) except Exception as e: return custom_error_response(str(e), response_status=status.HTTP_400_BAD_REQUEST) From f2cf1488e9dbf5c75c20a202cdb25445f0cf95d3 Mon Sep 17 00:00:00 2001 From: abutalib-kiwi Date: Mon, 7 Aug 2023 15:05:41 +0530 Subject: [PATCH 23/61] changes in edit api, included first name, last name and username --- .../serializers/user_management_serializer.py | 84 ++++++++++++++++--- 1 file changed, 72 insertions(+), 12 deletions(-) diff --git a/web_admin/serializers/user_management_serializer.py b/web_admin/serializers/user_management_serializer.py index 9333155..4bb0709 100644 --- a/web_admin/serializers/user_management_serializer.py +++ b/web_admin/serializers/user_management_serializer.py @@ -97,14 +97,18 @@ class GuardianSerializer(serializers.ModelSerializer): guardian serializer """ name = serializers.SerializerMethodField() - email = serializers.CharField(required=False) + first_name = serializers.SerializerMethodField() + last_name = serializers.SerializerMethodField() + username = serializers.SerializerMethodField() + email = serializers.EmailField(required=False) class Meta: """ meta class """ model = Guardian - fields = ('id', 'name', 'dob', 'gender', 'country_code', 'phone', 'is_active', 'country_name', 'image', 'email') + fields = ('id', 'name', 'first_name', 'last_name', 'username', 'dob', 'gender', 'country_code', 'phone', + 'is_active', 'country_name', 'image', 'email') def validate(self, attrs): """ @@ -137,25 +141,53 @@ class GuardianSerializer(serializers.ModelSerializer): @staticmethod def get_name(obj): """ - :param obj: user object + :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 + @staticmethod + def get_first_name(obj): + """ + :param obj: guardian object + :return: first name + """ + return obj.user.first_name + + @staticmethod + def get_last_name(obj): + """ + :param obj: guardian object + :return: last name + """ + return obj.user.last_name + + @staticmethod + def get_username(obj): + """ + :param obj: guardian object + :return: email + """ + return obj.user.username + class JuniorSerializer(serializers.ModelSerializer): """ junior serializer """ name = serializers.SerializerMethodField() - email = serializers.CharField(required=False) + first_name = serializers.SerializerMethodField() + last_name = serializers.SerializerMethodField() + username = serializers.SerializerMethodField() + email = serializers.EmailField(required=False) class Meta: """ meta class """ model = Junior - fields = ('id', 'name', 'dob', 'gender', 'country_code', 'phone', 'is_active', 'country_name', 'image', 'email') + fields = ('id', 'name', 'first_name', 'last_name', 'username', 'dob', 'gender', 'country_code', 'phone', + 'is_active', 'country_name', 'image', 'email') def validate(self, attrs): """ @@ -187,11 +219,35 @@ class JuniorSerializer(serializers.ModelSerializer): @staticmethod def get_name(obj): """ - :param obj: user object + :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 + @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 + + @staticmethod + def get_username(obj): + """ + :param obj: junior object + :return: email + """ + return obj.auth.username + class UserManagementDetailSerializer(serializers.ModelSerializer): """ @@ -229,10 +285,14 @@ class UserManagementDetailSerializer(serializers.ModelSerializer): :return: associated user """ if profile := obj.guardian_profile.all().first(): - junior = Junior.objects.filter(guardian_code__contains=[profile.guardian_code], is_verified=True) - serializer = JuniorSerializer(junior, many=True) - return serializer.data + if profile.guardian_code: + junior = Junior.objects.filter(guardian_code__contains=[profile.guardian_code], is_verified=True) + serializer = JuniorSerializer(junior, many=True) + return serializer.data elif profile := obj.junior_profile.all().first(): - guardian = Guardian.objects.filter(guardian_code__in=profile.guardian_code, is_verified=True) - serializer = GuardianSerializer(guardian, many=True) - return serializer.data + if profile.guardian_code: + guardian = Guardian.objects.filter(guardian_code__in=profile.guardian_code, is_verified=True) + serializer = GuardianSerializer(guardian, many=True) + return serializer.data + else: + return None From ae0fc4fe8d02f2aff2f2594a3fb63ac041f3c25e Mon Sep 17 00:00:00 2001 From: jain Date: Mon, 7 Aug 2023 17:06:31 +0530 Subject: [PATCH 24/61] article list api --- notifications/serializers.py | 4 --- notifications/views.py | 13 +++------ web_admin/serializers/article_serializer.py | 19 ++++++++++++ web_admin/urls.py | 4 +-- web_admin/views/article.py | 32 ++++++++++++++++++++- 5 files changed, 56 insertions(+), 16 deletions(-) diff --git a/notifications/serializers.py b/notifications/serializers.py index a085cf6..ff05dcd 100644 --- a/notifications/serializers.py +++ b/notifications/serializers.py @@ -41,7 +41,3 @@ class ReadNotificationSerializer(serializers.ModelSerializer): """Meta class""" model = Notification fields = ('id',) - def update(self, instance, validated_data): - instance.is_read = True - instance.save() - return instance diff --git a/notifications/views.py b/notifications/views.py index d5477b8..3d2ff4a 100644 --- a/notifications/views.py +++ b/notifications/views.py @@ -62,15 +62,10 @@ class ReadNotification(views.APIView): def put(self, request, format=None): try: - notification_id = str(self.request.data.get('notification_id')) - notification_queryset = Notification.objects.filter(id=notification_id, notification_to=self.request.user).last() + 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: - # use ReadNotificationSerializer serializer - serializer = ReadNotificationSerializer(notification_queryset, data=request.data, partial=True) - if serializer.is_valid(): - # save serializer - serializer.save() - return custom_response(SUCCESS_CODE['3039'], response_status=status.HTTP_200_OK) - return custom_error_response(serializer.errors, response_status=status.HTTP_400_BAD_REQUEST) + 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) diff --git a/web_admin/serializers/article_serializer.py b/web_admin/serializers/article_serializer.py index 427f20d..5b42686 100644 --- a/web_admin/serializers/article_serializer.py +++ b/web_admin/serializers/article_serializer.py @@ -214,3 +214,22 @@ 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) + total_points = serializers.SerializerMethodField('get_total_points') + + class Meta: + """ + meta class + """ + model = Article + fields = ('id', 'title', 'description', 'article_cards', 'total_points') + + def get_total_points(self, obj): + """total points of article""" + total_question = ArticleSurvey.objects.filter(article=obj).count() + return total_question * 5 diff --git a/web_admin/urls.py b/web_admin/urls.py index 30645a2..065d30d 100644 --- a/web_admin/urls.py +++ b/web_admin/urls.py @@ -6,7 +6,7 @@ from django.urls import path, include from rest_framework import routers # local imports -from web_admin.views.article import ArticleViewSet, DefaultArticleCardImagesViewSet +from web_admin.views.article import ArticleViewSet, DefaultArticleCardImagesViewSet, ArticleListViewSet from web_admin.views.auth import ForgotAndResetPasswordViewSet from web_admin.views.user_management import UserManagementViewSet @@ -16,7 +16,7 @@ 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('article-list', ArticleListViewSet, basename='article-list') # forgot and reset password api for admin router.register('admin', ForgotAndResetPasswordViewSet, basename='admin') diff --git a/web_admin/views/article.py b/web_admin/views/article.py index 53da6e1..f334650 100644 --- a/web_admin/views/article.py +++ b/web_admin/views/article.py @@ -16,7 +16,7 @@ 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) + DefaultArticleCardImageSerializer, ArticleListSerializer) USER = get_user_model() @@ -195,3 +195,33 @@ class DefaultArticleCardImagesViewSet(GenericViewSet, mixins.CreateModelMixin, m return custom_response(None, data=serializer.data) +class ArticleListViewSet(GenericViewSet, mixins.ListModelMixin): + """ + article api + """ + serializer_class = ArticleListSerializer + permission_classes = [IsAuthenticated,] + queryset = Article + 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) + return queryset + + def list(self, request, *args, **kwargs): + """ + article list api method + :param request: + :param args: + :param kwargs: + :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) From f91724426530c10ecdb6322a47875069b6f1936d Mon Sep 17 00:00:00 2001 From: jain Date: Mon, 7 Aug 2023 19:12:14 +0530 Subject: [PATCH 25/61] junior article points table --- guardian/views.py | 2 +- junior/migrations/0019_juniorarticlepoints.py | 30 +++++++++++++++++++ junior/models.py | 22 +++++++++++++- 3 files changed, 52 insertions(+), 2 deletions(-) create mode 100644 junior/migrations/0019_juniorarticlepoints.py diff --git a/guardian/views.py b/guardian/views.py index 1fa4e1b..ee86199 100644 --- a/guardian/views.py +++ b/guardian/views.py @@ -345,6 +345,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(ERROR_CODE['2068'], response_status=status.HTTP_200_OK) except Exception as e: return custom_error_response(str(e), response_status=status.HTTP_400_BAD_REQUEST) diff --git a/junior/migrations/0019_juniorarticlepoints.py b/junior/migrations/0019_juniorarticlepoints.py new file mode 100644 index 0000000..1112702 --- /dev/null +++ b/junior/migrations/0019_juniorarticlepoints.py @@ -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')), + ], + ), + ] diff --git a/junior/models.py b/junior/models.py index b18cbf5..908822c 100644 --- a/junior/models.py +++ b/junior/models.py @@ -9,7 +9,8 @@ from django.contrib.postgres.fields import ArrayField from base.constants import GENDERS, SIGNUP_METHODS, RELATIONSHIP # Import guardian's model from guardian.models import Guardian - +# Import web admin's model +from web_admin.models import SurveyOption, ArticleSurvey, Article """Define User model""" User = get_user_model() # Create your models here. @@ -137,3 +138,22 @@ 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.CASCADE, 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) + + def __str__(self): + """Return title""" + return f'{self.id} | {self.question}' From 659641467525ee06c6e2104f9d96788394cd97a3 Mon Sep 17 00:00:00 2001 From: jain Date: Tue, 8 Aug 2023 11:46:27 +0530 Subject: [PATCH 26/61] guardian list api changes --- base/constants.py | 6 +++ guardian/serializers.py | 39 +++++++++++++++++-- guardian/views.py | 4 +- .../0020_junior_guardian_code_status.py | 18 +++++++++ junior/models.py | 5 ++- junior/serializers.py | 12 ++++-- web_admin/serializers/article_serializer.py | 7 +++- 7 files changed, 80 insertions(+), 11 deletions(-) create mode 100644 junior/migrations/0020_junior_guardian_code_status.py diff --git a/base/constants.py b/base/constants.py index 6022f62..e38cb29 100644 --- a/base/constants.py +++ b/base/constants.py @@ -70,6 +70,12 @@ 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') +) # relationship RELATIONSHIP = ( ('1', 'parent'), diff --git a/guardian/serializers.py b/guardian/serializers.py index 04acb9d..1fa385b 100644 --- a/guardian/serializers.py +++ b/guardian/serializers.py @@ -394,11 +394,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): @@ -413,9 +422,33 @@ 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""" + return obj.junior.guardian_code_status diff --git a/guardian/views.py b/guardian/views.py index ee86199..775932a 100644 --- a/guardian/views.py +++ b/guardian/views.py @@ -34,7 +34,7 @@ from account.models import UserEmailOtp, UserNotification from .tasks import generate_otp from account.utils import custom_response, custom_error_response, OTP_EXPIRY, send_otp_email from base.messages import ERROR_CODE, SUCCESS_CODE -from base.constants import NUMBER +from base.constants import NUMBER, GUARDIAN_CODE_STATUS from .utils import upload_image_to_alibaba from notifications.constants import REGISTRATION, TASK_CREATED, LEADERBOARD_RANKING from notifications.utils import send_notification @@ -345,6 +345,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_response(ERROR_CODE['2068'], response_status=status.HTTP_200_OK) + return custom_response({"status": GUARDIAN_CODE_STATUS['1']},response_status=status.HTTP_200_OK) except Exception as e: return custom_error_response(str(e), response_status=status.HTTP_400_BAD_REQUEST) diff --git a/junior/migrations/0020_junior_guardian_code_status.py b/junior/migrations/0020_junior_guardian_code_status.py new file mode 100644 index 0000000..62e09d3 --- /dev/null +++ b/junior/migrations/0020_junior_guardian_code_status.py @@ -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), + ), + ] diff --git a/junior/models.py b/junior/models.py index 908822c..3adf459 100644 --- a/junior/models.py +++ b/junior/models.py @@ -6,7 +6,7 @@ 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 # Import guardian's model from guardian.models import Guardian # Import web admin's model @@ -74,6 +74,9 @@ class Junior(models.Model): 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 = models.CharField(max_length=31, choices=GUARDIAN_CODE_STATUS, default='1', + 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/junior/serializers.py b/junior/serializers.py index 1dc04da..178ba14 100644 --- a/junior/serializers.py +++ b/junior/serializers.py @@ -12,7 +12,8 @@ from account.utils import send_otp_email, generate_code from junior.models import Junior, JuniorPoints, JuniorGuardianRelationship 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) from guardian.models import Guardian, JuniorTask from account.models import UserEmailOtp, UserNotification from junior.utils import junior_notification_email, junior_approval_mail @@ -280,7 +281,8 @@ class AddJuniorSerializer(serializers.ModelSerializer): 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=GUARDIAN_CODE_STATUS['2']) JuniorGuardianRelationship.objects.create(guardian=guardian_data, junior=junior_data, relationship=relationship) """Generate otp""" @@ -399,13 +401,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['3'] + 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: diff --git a/web_admin/serializers/article_serializer.py b/web_admin/serializers/article_serializer.py index 5b42686..c338d04 100644 --- a/web_admin/serializers/article_serializer.py +++ b/web_admin/serializers/article_serializer.py @@ -221,15 +221,20 @@ class ArticleListSerializer(serializers.ModelSerializer): """ article_cards = ArticleCardSerializer(many=True) total_points = serializers.SerializerMethodField('get_total_points') + is_completed = serializers.SerializerMethodField('get_is_completed') class Meta: """ meta class """ model = Article - fields = ('id', 'title', 'description', 'article_cards', 'total_points') + fields = ('id', 'title', 'description', 'article_cards', 'total_points', 'is_completed') def get_total_points(self, obj): """total points of article""" total_question = ArticleSurvey.objects.filter(article=obj).count() return total_question * 5 + + def get_is_completed(self, obj): + """complete all question""" + return False \ No newline at end of file From f57b111555aaca2e4c259c5bc53b45de2b534424 Mon Sep 17 00:00:00 2001 From: jain Date: Tue, 8 Aug 2023 11:48:53 +0530 Subject: [PATCH 27/61] guardian list api changes --- web_admin/serializers/article_serializer.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/web_admin/serializers/article_serializer.py b/web_admin/serializers/article_serializer.py index c338d04..695401e 100644 --- a/web_admin/serializers/article_serializer.py +++ b/web_admin/serializers/article_serializer.py @@ -233,7 +233,7 @@ class ArticleListSerializer(serializers.ModelSerializer): def get_total_points(self, obj): """total points of article""" total_question = ArticleSurvey.objects.filter(article=obj).count() - return total_question * 5 + return total_question * NUMBER['five'] def get_is_completed(self, obj): """complete all question""" From 85e4ae876135775e1ac48424384cba335e40c0d0 Mon Sep 17 00:00:00 2001 From: jain Date: Tue, 8 Aug 2023 13:54:04 +0530 Subject: [PATCH 28/61] junior position changes and decrease api response time --- account/views.py | 9 ---- guardian/serializers.py | 6 ++- guardian/views.py | 3 -- junior/admin.py | 6 ++- junior/urls.py | 4 +- junior/views.py | 49 +++++++++++++++++---- web_admin/models.py | 4 +- web_admin/serializers/article_serializer.py | 2 +- 8 files changed, 55 insertions(+), 28 deletions(-) diff --git a/account/views.py b/account/views.py index 61c4e0e..b36d75d 100644 --- a/account/views.py +++ b/account/views.py @@ -245,7 +245,6 @@ class ForgotPasswordAPIView(views.APIView): 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 +261,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: @@ -376,7 +374,6 @@ class AdminLoginViewSet(viewsets.GenericViewSet): class UserEmailVerification(viewsets.ModelViewSet): """User Email verification""" serializer_class = EmailVerificationSerializer - queryset = UserEmailOtp.objects.all() def list(self, request, *args, **kwargs): try: @@ -419,7 +416,6 @@ class UserEmailVerification(viewsets.ModelViewSet): class ReSendEmailOtp(viewsets.ModelViewSet): """Send otp on phone""" - queryset = UserEmailOtp.objects.all() serializer_class = EmailVerificationSerializer permission_classes = [IsAuthenticated] @@ -442,7 +438,6 @@ class ReSendEmailOtp(viewsets.ModelViewSet): class ProfileAPIViewSet(viewsets.ModelViewSet): """Profile viewset""" - queryset = User.objects.all() serializer_class = JuniorProfileSerializer permission_classes = [IsAuthenticated] @@ -461,7 +456,6 @@ class ProfileAPIViewSet(viewsets.ModelViewSet): class UploadImageAPIViewSet(viewsets.ModelViewSet): """upload task image""" - queryset = DefaultTaskImages.objects.all() serializer_class = DefaultTaskImagesSerializer def create(self, request, *args, **kwargs): """upload images""" @@ -480,7 +474,6 @@ class UploadImageAPIViewSet(viewsets.ModelViewSet): class DefaultImageAPIViewSet(viewsets.ModelViewSet): """Profile viewset""" - queryset = DefaultTaskImages.objects.all() serializer_class = DefaultTaskImagesDetailsSerializer permission_classes = [IsAuthenticated] def list(self, request, *args, **kwargs): @@ -511,7 +504,6 @@ class DeleteUserProfileAPIViewSet(viewsets.GenericViewSet): class UserNotificationAPIViewSet(viewsets.ModelViewSet): """notification viewset""" - queryset = UserNotification.objects.all() serializer_class = UserNotificationSerializer permission_classes = [IsAuthenticated] def list(self, request, *args, **kwargs): @@ -523,7 +515,6 @@ class UserNotificationAPIViewSet(viewsets.ModelViewSet): class UpdateUserNotificationAPIViewSet(viewsets.ModelViewSet): """Update notification viewset""" - queryset = UserNotification.objects.all() serializer_class = UpdateUserNotificationSerializer permission_classes = [IsAuthenticated] diff --git a/guardian/serializers.py b/guardian/serializers.py index 1fa385b..65dd1ea 100644 --- a/guardian/serializers.py +++ b/guardian/serializers.py @@ -67,8 +67,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), diff --git a/guardian/views.py b/guardian/views.py index 775932a..9b55cf6 100644 --- a/guardian/views.py +++ b/guardian/views.py @@ -83,7 +83,6 @@ class SignupViewset(viewsets.ModelViewSet): class UpdateGuardianProfile(viewsets.ViewSet): """Update guardian profile""" - queryset = Guardian.objects.all() serializer_class = CreateGuardianSerializer permission_classes = [IsAuthenticated] @@ -117,7 +116,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): @@ -229,7 +227,6 @@ class TopJuniorListAPIView(viewsets.ModelViewSet): """Top juniors list""" serializer_class = TopJuniorSerializer permission_classes = [IsAuthenticated] - queryset = JuniorPoints.objects.all() def get_serializer_context(self): # context list diff --git a/junior/admin.py b/junior/admin.py index acd8734..5fdfcce 100644 --- a/junior/admin.py +++ b/junior/admin.py @@ -2,7 +2,7 @@ """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 # Register your models here. @admin.register(Junior) class JuniorAdmin(admin.ModelAdmin): @@ -27,3 +27,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'] diff --git a/junior/urls.py b/junior/urls.py index f66fa30..267faef 100644 --- a/junior/urls.py +++ b/junior/urls.py @@ -4,7 +4,7 @@ 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) """Third party import""" from rest_framework import routers @@ -41,6 +41,8 @@ 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') # Define url pattern""" urlpatterns = [ path('api/v1/', include(router.urls)), diff --git a/junior/views.py b/junior/views.py index 15cd387..449ac5f 100644 --- a/junior/views.py +++ b/junior/views.py @@ -27,8 +27,8 @@ 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 junior.models import Junior, JuniorPoints, JuniorGuardianRelationship, JuniorArticlePoints +from .serializers import (CreateJuniorSerializer, JuniorDetailListSerializer, AddJuniorSerializer, RemoveJuniorSerializer, CompleteTaskSerializer, JuniorPointsSerializer, AddGuardianSerializer, StartTaskSerializer, ReAssignTaskSerializer) from guardian.models import Guardian, JuniorTask @@ -40,7 +40,7 @@ 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 web_admin.models import Article, ArticleSurvey, SurveyOption """ Define APIs """ # Define validate guardian code API, # update junior profile, @@ -59,7 +59,6 @@ from notifications.constants import REMOVE_JUNIOR # Create your views here. class UpdateJuniorProfile(viewsets.ViewSet): """Update junior profile""" - queryset = Junior.objects.all() serializer_class = CreateJuniorSerializer permission_classes = [IsAuthenticated] @@ -93,7 +92,6 @@ class UpdateJuniorProfile(viewsets.ViewSet): class ValidateGuardianCode(viewsets.ViewSet): """Check guardian code exist or not""" - queryset = Guardian.objects.all() permission_classes = [IsAuthenticated] def list(self, request, *args, **kwargs): @@ -115,7 +113,6 @@ class JuniorListAPIView(viewsets.ModelViewSet): """Junior list of assosicated guardian""" serializer_class = JuniorDetailListSerializer - queryset = Junior.objects.all() permission_classes = [IsAuthenticated] filter_backends = (SearchFilter,) search_fields = ['auth__first_name', 'auth__last_name'] @@ -180,7 +177,6 @@ class InvitedJuniorAPIView(viewsets.ModelViewSet): """Junior list of assosicated guardian""" serializer_class = JuniorDetailListSerializer - queryset = Junior.objects.all() permission_classes = [IsAuthenticated] pagination_class = PageNumberPagination http_method_names = ('get',) @@ -210,7 +206,6 @@ class FilterJuniorAPIView(viewsets.ModelViewSet): serializer_class = JuniorDetailListSerializer permission_classes = [IsAuthenticated] pagination_class = PageNumberPagination - queryset = Junior.objects.all() http_method_names = ('get',) def get_queryset(self): @@ -268,7 +263,6 @@ class JuniorTaskListAPIView(viewsets.ModelViewSet): serializer_class = TaskDetailsjuniorSerializer permission_classes = [IsAuthenticated] pagination_class = PageNumberPagination - queryset = JuniorTask.objects.all() http_method_names = ('get',) def list(self, request, *args, **kwargs): @@ -458,3 +452,40 @@ 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""" + serializer_class = AddGuardianSerializer + permission_classes = [IsAuthenticated] + http_method_names = ('post',) + + def create(self, request, *args, **kwargs): + """ junior list""" + 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: + print("article_data====>", article_data,'===>',type(article_data)) + question_query = ArticleSurvey.objects.filter(article=article_id) + print("question_query====>",question_query,'===>',type(question_query)) + for question in question_query: + print("question====>", question, '===>', type(question)) + answer = SurveyOption.objects.filter(survey=question) + print("answer===>",answer,'====>',type(answer)) + for ans in answer: + JuniorArticlePoints.objects.create(junior=junior_instance, + article=article_data, + question=question, + submitted_answer=ans) + print("CREATED CREATED") + return custom_response(SUCCESS_CODE['3034'], response_status=status.HTTP_200_OK) + # use AddJuniorSerializer serializer + # serializer = AddGuardianSerializer(data=request.data, context=info) + # if serializer.is_valid(): + # # save serializer + # serializer.save() + # return custom_response(SUCCESS_CODE['3034'], serializer.data, response_status=status.HTTP_200_OK) + # return custom_error_response(serializer.error, response_status=status.HTTP_400_BAD_REQUEST) + except Exception as e: + return custom_error_response(str(e), response_status=status.HTTP_400_BAD_REQUEST) diff --git a/web_admin/models.py b/web_admin/models.py index 8a3bb16..5dbef97 100644 --- a/web_admin/models.py +++ b/web_admin/models.py @@ -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): diff --git a/web_admin/serializers/article_serializer.py b/web_admin/serializers/article_serializer.py index 695401e..b39d20d 100644 --- a/web_admin/serializers/article_serializer.py +++ b/web_admin/serializers/article_serializer.py @@ -237,4 +237,4 @@ class ArticleListSerializer(serializers.ModelSerializer): def get_is_completed(self, obj): """complete all question""" - return False \ No newline at end of file + return False From 10a1ea9b765728695bcc1fb622ffe67b17d7a2c2 Mon Sep 17 00:00:00 2001 From: abutalib-kiwi Date: Tue, 8 Aug 2023 14:29:57 +0530 Subject: [PATCH 29/61] change in admin login --- account/views.py | 27 ++++++++++----------------- 1 file changed, 10 insertions(+), 17 deletions(-) diff --git a/account/views.py b/account/views.py index 61c4e0e..853c43a 100644 --- a/account/views.py +++ b/account/views.py @@ -329,24 +329,17 @@ class UserLogin(viewsets.ViewSet): @action(methods=['post'], detail=False) def admin_login(self, request): - username = request.data.get('username') + email = request.data.get('email') password = request.data.get('password') - user = authenticate(request, username=username, password=password) - try: - if user is not None: - login(request, user) - if user.is_superuser: - serializer = SuperUserSerializer(user) - return custom_response(SUCCESS_CODE['3003'], serializer.data, response_status=status.HTTP_200_OK) - else: - return custom_error_response(ERROR_CODE["2002"], response_status=status.HTTP_401_UNAUTHORIZED) - except Exception as e: - logging.error(e) - refresh = RefreshToken.for_user(user) - access_token = str(refresh.access_token) - refresh_token = str(refresh) - data = {"auth_token": access_token, "refresh_token":refresh_token, "user_type": '3'} - return custom_response(None, data, response_status=status.HTTP_200_OK) + user = User.objects.filter(email__iexact=email, is_superuser=True + ).only('id', 'first_name', 'last_name', 'email', 'is_superuser').first() + + if not user: + return custom_error_response(ERROR_CODE["2063"], response_status=status.HTTP_400_BAD_REQUEST) + elif not user.check_password(password): + return custom_error_response(ERROR_CODE["2031"], response_status=status.HTTP_400_BAD_REQUEST) + serializer = SuperUserSerializer(user) + return custom_response(SUCCESS_CODE['3003'], serializer.data, response_status=status.HTTP_200_OK) class AdminLoginViewSet(viewsets.GenericViewSet): From 4b23394569bc345d29de5e48d9758cf5bdedad46 Mon Sep 17 00:00:00 2001 From: jain Date: Tue, 8 Aug 2023 15:03:24 +0530 Subject: [PATCH 30/61] top junior api --- guardian/views.py | 1 + 1 file changed, 1 insertion(+) diff --git a/guardian/views.py b/guardian/views.py index 9b55cf6..076f563 100644 --- a/guardian/views.py +++ b/guardian/views.py @@ -225,6 +225,7 @@ class SearchTaskListAPIView(viewsets.ModelViewSet): class TopJuniorListAPIView(viewsets.ModelViewSet): """Top juniors list""" + queryset = JuniorPoints.objects.all() serializer_class = TopJuniorSerializer permission_classes = [IsAuthenticated] From 7d428f5eb5bc6b5776e955c4ac38043a8b3a1c8e Mon Sep 17 00:00:00 2001 From: jain Date: Tue, 8 Aug 2023 16:05:56 +0530 Subject: [PATCH 31/61] junior list api --- base/messages.py | 3 ++- ...er_juniorarticlepoints_submitted_answer.py | 20 ++++++++++++++++ junior/models.py | 3 ++- junior/serializers.py | 4 ++-- junior/views.py | 24 +++++-------------- 5 files changed, 32 insertions(+), 22 deletions(-) create mode 100644 junior/migrations/0021_alter_juniorarticlepoints_submitted_answer.py diff --git a/base/messages.py b/base/messages.py index cf85f2b..05b4ec3 100644 --- a/base/messages.py +++ b/base/messages.py @@ -149,7 +149,8 @@ SUCCESS_CODE = { "3037": "Profile has been updated successfully.", "3038": "Status has been changed successfully.", # notification read - "3039": "Notification read successfully" + "3039": "Notification read successfully", + "3040": "Start article successfully" } """status code error""" STATUS_CODE_ERROR = { diff --git a/junior/migrations/0021_alter_juniorarticlepoints_submitted_answer.py b/junior/migrations/0021_alter_juniorarticlepoints_submitted_answer.py new file mode 100644 index 0000000..45ce9a4 --- /dev/null +++ b/junior/migrations/0021_alter_juniorarticlepoints_submitted_answer.py @@ -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'), + ), + ] diff --git a/junior/models.py b/junior/models.py index 3adf459..3d2aaa4 100644 --- a/junior/models.py +++ b/junior/models.py @@ -149,7 +149,8 @@ class JuniorArticlePoints(models.Model): 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.CASCADE, related_name='submitted_answer') + 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) diff --git a/junior/serializers.py b/junior/serializers.py index 178ba14..f8be6aa 100644 --- a/junior/serializers.py +++ b/junior/serializers.py @@ -282,7 +282,7 @@ class AddJuniorSerializer(serializers.ModelSerializer): referral_code=generate_code(ZOD, user_data.id), referral_code_used=guardian_data.referral_code, is_password_set=False, is_verified=True, - guardian_code_status=GUARDIAN_CODE_STATUS['2']) + guardian_code_status=GUARDIAN_CODE_STATUS[1][0]) JuniorGuardianRelationship.objects.create(guardian=guardian_data, junior=junior_data, relationship=relationship) """Generate otp""" @@ -401,7 +401,7 @@ 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['3'] + junior_data.guardian_code_status = GUARDIAN_CODE_STATUS[2][0] junior_data.save() instance = User.objects.filter(username=email).last() if instance: diff --git a/junior/views.py b/junior/views.py index 449ac5f..98428ce 100644 --- a/junior/views.py +++ b/junior/views.py @@ -113,6 +113,7 @@ class JuniorListAPIView(viewsets.ModelViewSet): """Junior list of assosicated guardian""" serializer_class = JuniorDetailListSerializer + queryset = Junior.objects.all() permission_classes = [IsAuthenticated] filter_backends = (SearchFilter,) search_fields = ['auth__first_name', 'auth__last_name'] @@ -455,7 +456,6 @@ class ReAssignJuniorTaskAPIView(views.APIView): class StartArticleAPIView(viewsets.ModelViewSet): """Start article""" - serializer_class = AddGuardianSerializer permission_classes = [IsAuthenticated] http_method_names = ('post',) @@ -466,26 +466,14 @@ class StartArticleAPIView(viewsets.ModelViewSet): article_id = request.data.get('article_id') article_data = Article.objects.filter(id=article_id).last() if article_data: - print("article_data====>", article_data,'===>',type(article_data)) question_query = ArticleSurvey.objects.filter(article=article_id) - print("question_query====>",question_query,'===>',type(question_query)) for question in question_query: - print("question====>", question, '===>', type(question)) - answer = SurveyOption.objects.filter(survey=question) - print("answer===>",answer,'====>',type(answer)) - for ans in answer: + if not JuniorArticlePoints.objects.filter(junior=junior_instance, + article=article_data, + question=question): JuniorArticlePoints.objects.create(junior=junior_instance, article=article_data, - question=question, - submitted_answer=ans) - print("CREATED CREATED") - return custom_response(SUCCESS_CODE['3034'], response_status=status.HTTP_200_OK) - # use AddJuniorSerializer serializer - # serializer = AddGuardianSerializer(data=request.data, context=info) - # if serializer.is_valid(): - # # save serializer - # serializer.save() - # return custom_response(SUCCESS_CODE['3034'], serializer.data, response_status=status.HTTP_200_OK) - # return custom_error_response(serializer.error, response_status=status.HTTP_400_BAD_REQUEST) + 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) From f75201b3dd0114078133a25c9da683dbf7e125ca Mon Sep 17 00:00:00 2001 From: jain Date: Tue, 8 Aug 2023 16:36:03 +0530 Subject: [PATCH 32/61] changes in add junior api --- guardian/views.py | 2 +- junior/serializers.py | 5 +++-- junior/views.py | 17 ++++++++++++++--- 3 files changed, 18 insertions(+), 6 deletions(-) diff --git a/guardian/views.py b/guardian/views.py index 076f563..3e265b0 100644 --- a/guardian/views.py +++ b/guardian/views.py @@ -343,6 +343,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_response({"status": GUARDIAN_CODE_STATUS['1']},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) diff --git a/junior/serializers.py b/junior/serializers.py index f8be6aa..24e8b21 100644 --- a/junior/serializers.py +++ b/junior/serializers.py @@ -254,11 +254,10 @@ class JuniorProfileSerializer(serializers.ModelSerializer): 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): @@ -267,6 +266,7 @@ 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, @@ -276,6 +276,7 @@ class AddJuniorSerializer(serializers.ModelSerializer): user_data.set_password(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), diff --git a/junior/views.py b/junior/views.py index 98428ce..e1cb8e0 100644 --- a/junior/views.py +++ b/junior/views.py @@ -149,8 +149,19 @@ class AddJuniorAPIView(viewsets.ModelViewSet): def create(self, request, *args, **kwargs): """ junior list""" 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) return custom_response(SUCCESS_CODE['3021'], response_status=status.HTTP_200_OK) @@ -160,7 +171,7 @@ 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) From d86f082e587fc78971faa7cdae8a615bbc66bd17 Mon Sep 17 00:00:00 2001 From: jain Date: Wed, 9 Aug 2023 13:37:06 +0530 Subject: [PATCH 33/61] article card api, check answer api --- junior/serializers.py | 3 ++ junior/urls.py | 7 ++- junior/utils.py | 2 +- junior/views.py | 59 +++++++++++++++++++- web_admin/serializers/article_serializer.py | 60 +++++++++++++++++++-- web_admin/urls.py | 4 +- web_admin/views/article.py | 27 ++++++++-- 7 files changed, 152 insertions(+), 10 deletions(-) diff --git a/junior/serializers.py b/junior/serializers.py index 24e8b21..4014ee4 100644 --- a/junior/serializers.py +++ b/junior/serializers.py @@ -286,6 +286,8 @@ class AddJuniorSerializer(serializers.ModelSerializer): guardian_code_status=GUARDIAN_CODE_STATUS[1][0]) 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) @@ -479,3 +481,4 @@ class ReAssignTaskSerializer(serializers.ModelSerializer): instance.requested_on = None instance.save() return instance + diff --git a/junior/urls.py b/junior/urls.py index 267faef..76e5b47 100644 --- a/junior/urls.py +++ b/junior/urls.py @@ -4,7 +4,8 @@ from django.urls import path, include from .views import (UpdateJuniorProfile, ValidateGuardianCode, JuniorListAPIView, AddJuniorAPIView, InvitedJuniorAPIView, FilterJuniorAPIView, RemoveJuniorAPIView, JuniorTaskListAPIView, CompleteJuniorTaskAPIView, JuniorPointsListAPIView, ValidateReferralCode, - InviteGuardianAPIView, StartTaskAPIView, ReAssignJuniorTaskAPIView, StartArticleAPIView) + InviteGuardianAPIView, StartTaskAPIView, ReAssignJuniorTaskAPIView, StartArticleAPIView, + StartAssessmentAPIView, CheckAnswerAPIView) """Third party import""" from rest_framework import routers @@ -43,6 +44,10 @@ router.register('validate-referral-code', ValidateReferralCode, basename='valida 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') # Define url pattern""" urlpatterns = [ path('api/v1/', include(router.urls)), diff --git a/junior/utils.py b/junior/utils.py index fefe950..621a6dd 100644 --- a/junior/utils.py +++ b/junior/utils.py @@ -50,7 +50,7 @@ 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', 'updated_at') # Now, iterate through the queryset and update the position field based on the order. position = 1 diff --git a/junior/views.py b/junior/views.py index e1cb8e0..9a7ce35 100644 --- a/junior/views.py +++ b/junior/views.py @@ -6,6 +6,7 @@ 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 @@ -41,6 +42,8 @@ from .utils import update_positions_based_on_points from notifications.utils import send_notification from notifications.constants import REMOVE_JUNIOR from web_admin.models import Article, ArticleSurvey, SurveyOption +from web_admin.serializers.article_serializer import (ArticleSerializer, ArticleListSerializer, + StartAssessmentSerializer) """ Define APIs """ # Define validate guardian code API, # update junior profile, @@ -351,6 +354,7 @@ class JuniorPointsListAPIView(viewsets.ModelViewSet): def get_queryset(self): """get queryset""" + update_positions_based_on_points() return JuniorTask.objects.filter(junior__auth__email=self.request.user).last() def list(self, request, *args, **kwargs): """profile view""" @@ -358,7 +362,6 @@ class JuniorPointsListAPIView(viewsets.ModelViewSet): try: queryset = self.get_queryset() # 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: @@ -488,3 +491,57 @@ class StartArticleAPIView(viewsets.ModelViewSet): 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): + """Junior Points 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_cards', 'article_survey', 'article_survey__options' + ).order_by('-created_at') + return article + def list(self, request, *args, **kwargs): + """profile view""" + + 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): + """Junior Points viewset""" + 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): + """profile view""" + + try: + answer_id = self.request.GET.get('answer_id') + queryset = self.get_queryset() + submit_ans = SurveyOption.objects.filter(id=answer_id, is_answer=True).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) + 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) diff --git a/web_admin/serializers/article_serializer.py b/web_admin/serializers/article_serializer.py index b39d20d..9458190 100644 --- a/web_admin/serializers/article_serializer.py +++ b/web_admin/serializers/article_serializer.py @@ -12,7 +12,7 @@ from base.messages import ERROR_CODE from guardian.utils import upload_image_to_alibaba 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 USER = get_user_model() @@ -64,7 +64,7 @@ class ArticleSurveySerializer(serializers.ModelSerializer): meta class """ model = ArticleSurvey - fields = ('id', 'question', 'options') + fields = ('id', 'question', 'options', 'points') class ArticleSerializer(serializers.ModelSerializer): @@ -220,6 +220,7 @@ class ArticleListSerializer(serializers.ModelSerializer): serializer for article API """ article_cards = ArticleCardSerializer(many=True) + article_survey = ArticleSurveySerializer(many=True) total_points = serializers.SerializerMethodField('get_total_points') is_completed = serializers.SerializerMethodField('get_is_completed') @@ -228,7 +229,7 @@ class ArticleListSerializer(serializers.ModelSerializer): meta class """ model = Article - fields = ('id', 'title', 'description', 'article_cards', 'total_points', 'is_completed') + fields = ('id', 'title', 'description', 'article_cards', 'article_survey', 'total_points', 'is_completed') def get_total_points(self, obj): """total points of article""" @@ -238,3 +239,56 @@ class ArticleListSerializer(serializers.ModelSerializer): def get_is_completed(self, obj): """complete all question""" 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') + + 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 + + class Meta: + """ + meta class + """ + model = ArticleSurvey + fields = ('id', 'question', 'options', 'points', 'is_attempt') + +class StartAssessmentSerializer(serializers.ModelSerializer): + """ + serializer for article API + """ + article_survey = ArticleQuestionSerializer(many=True) + + class Meta: + """ + meta class + """ + model = Article + fields = ('article_survey',) + + +class CheckAnswerSerializer(serializers.ModelSerializer): + """ + serializer for article API + """ + article_survey = ArticleSurveySerializer(many=True) + + class Meta: + """ + meta class + """ + model = ArticleSurvey + fields = ('id', 'article_survey') + + def create(self, validated_data): + """create function""" diff --git a/web_admin/urls.py b/web_admin/urls.py index 065d30d..6bf3b5e 100644 --- a/web_admin/urls.py +++ b/web_admin/urls.py @@ -6,7 +6,8 @@ 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.article import (ArticleViewSet, DefaultArticleCardImagesViewSet, ArticleListViewSet, + ArticleCardListViewSet) from web_admin.views.auth import ForgotAndResetPasswordViewSet from web_admin.views.user_management import UserManagementViewSet @@ -17,6 +18,7 @@ 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('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') diff --git a/web_admin/views/article.py b/web_admin/views/article.py index f334650..d4cc843 100644 --- a/web_admin/views/article.py +++ b/web_admin/views/article.py @@ -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 + ) USER = get_user_model() @@ -224,4 +225,24 @@ class ArticleListViewSet(GenericViewSet, mixins.ListModelMixin): 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) + return custom_response(None, data=serializer.data) + +class ArticleCardListViewSet(viewsets.ModelViewSet): + """Junior Points viewset""" + serializer_class = ArticleCardSerializer + 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): + """profile view""" + + try: + queryset = self.get_queryset() + # article card list + serializer = ArticleCardSerializer(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) From 881bda739b4d3d40ea042329a649a46155dc669c Mon Sep 17 00:00:00 2001 From: jain Date: Wed, 9 Aug 2023 13:48:02 +0530 Subject: [PATCH 34/61] article card api, check answer api --- base/constants.py | 4 ++-- guardian/serializers.py | 4 ++-- guardian/utils.py | 2 +- guardian/views.py | 44 +++++++++++++++++++---------------------- 4 files changed, 25 insertions(+), 29 deletions(-) diff --git a/base/constants.py b/base/constants.py index e38cb29..7efdb8b 100644 --- a/base/constants.py +++ b/base/constants.py @@ -112,7 +112,7 @@ 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' diff --git a/guardian/serializers.py b/guardian/serializers.py index 65dd1ea..172ccc4 100644 --- a/guardian/serializers.py +++ b/guardian/serializers.py @@ -22,7 +22,7 @@ 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 from junior.models import Junior, JuniorPoints, JuniorGuardianRelationship from .utils import real_time, convert_timedelta_into_datetime, update_referral_points # notification's constant @@ -83,7 +83,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", }) diff --git a/guardian/utils.py b/guardian/utils.py index 9a6ae05..57e8080 100644 --- a/guardian/utils.py +++ b/guardian/utils.py @@ -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 diff --git a/guardian/views.py b/guardian/views.py index 3e265b0..75a16ff 100644 --- a/guardian/views.py +++ b/guardian/views.py @@ -10,7 +10,6 @@ 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 django.utils import timezone # Import guardian's model, # Import junior's model, @@ -57,29 +56,26 @@ class SignupViewset(viewsets.ModelViewSet): serializer_class = UserSerializer 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) + 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) class UpdateGuardianProfile(viewsets.ViewSet): """Update guardian profile""" From c9ee482512be0ea68058f313d69ecd9d9c459493 Mon Sep 17 00:00:00 2001 From: abutalib-kiwi Date: Wed, 9 Aug 2023 14:14:13 +0530 Subject: [PATCH 35/61] otp, verify otp and login bug resolved --- account/serializers.py | 4 ++-- account/views.py | 4 ++-- base/messages.py | 14 +++++++------- web_admin/views/auth.py | 6 +++--- web_admin/views/user_management.py | 7 ++++--- 5 files changed, 18 insertions(+), 17 deletions(-) diff --git a/account/serializers.py b/account/serializers.py index 284ae09..5686931 100644 --- a/account/serializers.py +++ b/account/serializers.py @@ -155,9 +155,9 @@ class AdminLoginSerializer(serializers.ModelSerializer): ).only('id', 'first_name', 'last_name', 'email', 'is_superuser').first() if not user: - raise serializers.ValidationError({'details': ERROR_CODE['2063']}) + raise serializers.ValidationError({'details': ERROR_CODE['2002']}) elif not user.check_password(attrs['password']): - raise serializers.ValidationError({'details': ERROR_CODE['2031']}) + raise serializers.ValidationError({'details': ERROR_CODE['2002']}) self.context.update({'user': user}) return attrs diff --git a/account/views.py b/account/views.py index 853c43a..80530ad 100644 --- a/account/views.py +++ b/account/views.py @@ -335,9 +335,9 @@ class UserLogin(viewsets.ViewSet): ).only('id', 'first_name', 'last_name', 'email', 'is_superuser').first() if not user: - return custom_error_response(ERROR_CODE["2063"], response_status=status.HTTP_400_BAD_REQUEST) + return custom_error_response(ERROR_CODE["2002"], response_status=status.HTTP_400_BAD_REQUEST) elif not user.check_password(password): - return custom_error_response(ERROR_CODE["2031"], response_status=status.HTTP_400_BAD_REQUEST) + 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) diff --git a/base/messages.py b/base/messages.py index cf85f2b..f7d36dc 100644 --- a/base/messages.py +++ b/base/messages.py @@ -23,15 +23,15 @@ ERROR_CODE_REQUIRED = { # Error code ERROR_CODE = { - "2000": "Email not found.", + "2000": "Invalid email address. Please enter a registered email.", "2001": "This is your existing password. Please choose other one", - "2002": "Invalid login credentials.", + "2002": "Invalid username or password.", "2003": "An account already exists with this email address.", "2004": "User not found.", "2005": "Your account has been activated.", "2006": "Your account is not activated.", "2007": "Your account already activated.", - "2008": "Invalid OTP.", + "2008": "The OTP entered is not correct.", "2009": "The user provided cannot be found or the reset password token has become invalid/timed out.", "2010": "Invalid Link.", "2011": "Your profile has not been completed yet.", @@ -54,7 +54,7 @@ ERROR_CODE = { "2026": "New password should not same as old password", "2027": "data should contain `identityToken`", "2028": "You are not authorized person to sign up on this platform", - "2029": "Validity of otp verification is expired", + "2029": "Validity of otp verification has expired. Please request a new one.", "2030": "Use correct user type and token", # invalid password "2031": "Invalid password", @@ -111,7 +111,7 @@ SUCCESS_CODE = { # Success code for link verified "3005": "Your account is deleted successfully.", # Success code for password reset - "3006": "Your password has been reset successfully.", + "3006": "Password reset successful. You can now log in with your new password.", # Success code for password update "3007": "Your password has been changed successfully.", # Success code for valid link @@ -124,8 +124,8 @@ SUCCESS_CODE = { "3012": "Phone OTP Verified successfully", "3013": "Valid Guardian code", "3014": "Password has been updated successfully.", - "3015": "Verification code sent on your email.", - "3016": "Send otp on your Email successfully", + "3015": "Verification code has been sent on your email.", + "3016": "An OTP has been sent on your email.", "3017": "Profile image update successfully", "3018": "Task created successfully", "3019": "Support Email sent successfully", diff --git a/web_admin/views/auth.py b/web_admin/views/auth.py index 0273a08..fae973e 100644 --- a/web_admin/views/auth.py +++ b/web_admin/views/auth.py @@ -33,7 +33,7 @@ class ForgotAndResetPasswordViewSet(GenericViewSet): if serializer.is_valid(): serializer.save() return custom_response(SUCCESS_CODE['3015']) - return custom_error_response(ERROR_CODE['2063'], status.HTTP_400_BAD_REQUEST) + return custom_error_response(ERROR_CODE['2000'], status.HTTP_400_BAD_REQUEST) @action(methods=['post'], url_name='verify-otp', url_path='verify-otp', detail=False, serializer_class=AdminVerifyOTPSerializer) @@ -45,7 +45,7 @@ class ForgotAndResetPasswordViewSet(GenericViewSet): serializer = self.serializer_class(data=request.data) if serializer.is_valid(): return custom_response(SUCCESS_CODE['3011']) - return custom_error_response(ERROR_CODE['2063'], status.HTTP_400_BAD_REQUEST) + return custom_error_response(ERROR_CODE['2008'], status.HTTP_400_BAD_REQUEST) @action(methods=['post'], url_name='create-password', url_path='create-password', detail=False, serializer_class=AdminCreatePasswordSerializer) @@ -59,5 +59,5 @@ class ForgotAndResetPasswordViewSet(GenericViewSet): user = USER.objects.filter(email=serializer.validated_data.get('email')).first() user.set_password(serializer.validated_data.get('new_password')) user.save() - return custom_response(SUCCESS_CODE['3007']) + return custom_response(SUCCESS_CODE['3006']) return custom_error_response(ERROR_CODE['2064'], status.HTTP_400_BAD_REQUEST) diff --git a/web_admin/views/user_management.py b/web_admin/views/user_management.py index 20184cd..3bd8f64 100644 --- a/web_admin/views/user_management.py +++ b/web_admin/views/user_management.py @@ -55,11 +55,12 @@ class UserManagementViewSet(GenericViewSet, mixins.ListModelMixin, :return: """ queryset = self.get_queryset() - count = queryset.count() + 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) - return custom_response(None, data=serializer.data, count=count) + return custom_response(None, data=serializer.data, count=queryset.count()) def retrieve(self, request, *args, **kwargs): """ @@ -69,7 +70,7 @@ class UserManagementViewSet(GenericViewSet, mixins.ListModelMixin, :return: user details """ if self.request.query_params.get('user_type') not in [dict(USER_TYPE).get('1'), dict(USER_TYPE).get('2')]: - return custom_error_response('Action not allowed', status.HTTP_400_BAD_REQUEST) + 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']) From 3cb0e3ed8b86784c6781e01ea3022ef104f04ad9 Mon Sep 17 00:00:00 2001 From: jain Date: Wed, 9 Aug 2023 14:37:38 +0530 Subject: [PATCH 36/61] guardian code add in junior list api --- junior/serializers.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/junior/serializers.py b/junior/serializers.py index 4014ee4..3f374cc 100644 --- a/junior/serializers.py +++ b/junior/serializers.py @@ -207,7 +207,7 @@ class JuniorDetailListSerializer(serializers.ModelSerializer): fields = ['id', 'email', 'first_name', 'last_name', 'country_code', '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'] class JuniorProfileSerializer(serializers.ModelSerializer): """junior serializer""" From 69ce6857e8bd95ee83b3525d1ac27c5d5ab1779c Mon Sep 17 00:00:00 2001 From: jain Date: Wed, 9 Aug 2023 16:36:33 +0530 Subject: [PATCH 37/61] financial learning section api --- base/constants.py | 6 ++ base/messages.py | 8 +- guardian/serializers.py | 2 - junior/admin.py | 22 +++++- junior/migrations/0022_juniorarticle.py | 27 +++++++ junior/migrations/0023_juniorarticlecard.py | 27 +++++++ junior/models.py | 39 +++++++++- junior/serializers.py | 2 +- junior/urls.py | 7 +- junior/views.py | 84 ++++++++++++++++++++- 10 files changed, 211 insertions(+), 13 deletions(-) create mode 100644 junior/migrations/0022_juniorarticle.py create mode 100644 junior/migrations/0023_juniorarticlecard.py diff --git a/base/constants.py b/base/constants.py index 7efdb8b..36079b9 100644 --- a/base/constants.py +++ b/base/constants.py @@ -76,6 +76,12 @@ GUARDIAN_CODE_STATUS = ( ('2', 'exist guardian code'), ('3', 'request for guardian code') ) +# article status +ARTICLE_STATUS = ( + ('1', 'read'), + ('2', 'in_progress'), + ('3', 'completed') +) # relationship RELATIONSHIP = ( ('1', 'parent'), diff --git a/base/messages.py b/base/messages.py index 05b4ec3..1e3e146 100644 --- a/base/messages.py +++ b/base/messages.py @@ -150,7 +150,13 @@ SUCCESS_CODE = { "3038": "Status has been changed successfully.", # notification read "3039": "Notification read successfully", - "3040": "Start article successfully" + "3040": "Start article successfully", + # complete article + "3041": "Article completed successfully", + # submit assessment successfully + "3042": "Assessment completed successfully", + "3043": "Read article card successfully" + } """status code error""" STATUS_CODE_ERROR = { diff --git a/guardian/serializers.py b/guardian/serializers.py index 0a8b625..12d5383 100644 --- a/guardian/serializers.py +++ b/guardian/serializers.py @@ -387,8 +387,6 @@ class ApproveTaskSerializer(serializers.ModelSerializer): # 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) diff --git a/junior/admin.py b/junior/admin.py index 5fdfcce..6c6cdf9 100644 --- a/junior/admin.py +++ b/junior/admin.py @@ -2,8 +2,26 @@ """Third party Django app""" from django.contrib import admin """Import Django app""" -from .models import Junior, JuniorPoints, JuniorGuardianRelationship, JuniorArticlePoints +from .models import (Junior, JuniorPoints, JuniorGuardianRelationship, JuniorArticlePoints, JuniorArticle, + JuniorArticleCard) # Register your models here. +@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""" @@ -30,4 +48,4 @@ class JuniorGuardianRelationshipAdmin(admin.ModelAdmin): @admin.register(JuniorArticlePoints) class JuniorArticlePointsAdmin(admin.ModelAdmin): """Junior Admin""" - list_display = ['junior', 'article', 'question', 'submitted_answer'] + list_display = ['junior', 'article', 'question', 'submitted_answer', 'is_answer_correct'] diff --git a/junior/migrations/0022_juniorarticle.py b/junior/migrations/0022_juniorarticle.py new file mode 100644 index 0000000..9dd794e --- /dev/null +++ b/junior/migrations/0022_juniorarticle.py @@ -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')), + ], + ), + ] diff --git a/junior/migrations/0023_juniorarticlecard.py b/junior/migrations/0023_juniorarticlecard.py new file mode 100644 index 0000000..9b8a1af --- /dev/null +++ b/junior/migrations/0023_juniorarticlecard.py @@ -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')), + ], + ), + ] diff --git a/junior/models.py b/junior/models.py index 3d2aaa4..180b425 100644 --- a/junior/models.py +++ b/junior/models.py @@ -6,11 +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, GUARDIAN_CODE_STATUS +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 +from web_admin.models import SurveyOption, ArticleSurvey, Article, ArticleCard """Define User model""" User = get_user_model() # Create your models here. @@ -161,3 +161,38 @@ class JuniorArticlePoints(models.Model): 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.OneToOneField(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') + created_at = models.DateTimeField(auto_now_add=True) + updated_at = models.DateTimeField(auto_now=True) + + 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) + + def __str__(self): + """Return title""" + return f'{self.id} | {self.article}' diff --git a/junior/serializers.py b/junior/serializers.py index bfd79d1..079031a 100644 --- a/junior/serializers.py +++ b/junior/serializers.py @@ -12,7 +12,7 @@ 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 junior.models import Junior, JuniorPoints, JuniorGuardianRelationship, JuniorArticlePoints 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, diff --git a/junior/urls.py b/junior/urls.py index 76e5b47..e494a3d 100644 --- a/junior/urls.py +++ b/junior/urls.py @@ -5,7 +5,8 @@ from .views import (UpdateJuniorProfile, ValidateGuardianCode, JuniorListAPIView InvitedJuniorAPIView, FilterJuniorAPIView, RemoveJuniorAPIView, JuniorTaskListAPIView, CompleteJuniorTaskAPIView, JuniorPointsListAPIView, ValidateReferralCode, InviteGuardianAPIView, StartTaskAPIView, ReAssignJuniorTaskAPIView, StartArticleAPIView, - StartAssessmentAPIView, CheckAnswerAPIView) + StartAssessmentAPIView, CheckAnswerAPIView, CompleteArticleAPIView, ReadArticleCardAPIView, + CreateArticleCardAPIView) """Third party import""" from rest_framework import routers @@ -48,6 +49,8 @@ router.register('start-article', StartArticleAPIView, basename='start-article') 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') # Define url pattern""" urlpatterns = [ path('api/v1/', include(router.urls)), @@ -55,4 +58,6 @@ 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()), ] diff --git a/junior/views.py b/junior/views.py index 9a7ce35..5926c47 100644 --- a/junior/views.py +++ b/junior/views.py @@ -28,20 +28,23 @@ import requests # Import upload_image_to_alibaba # Import custom_response, custom_error_response # Import constants -from junior.models import Junior, JuniorPoints, JuniorGuardianRelationship, JuniorArticlePoints +from django.db.models import Sum +from junior.models import (Junior, JuniorPoints, JuniorGuardianRelationship, JuniorArticlePoints, JuniorArticle, + JuniorArticleCard) from .serializers import (CreateJuniorSerializer, JuniorDetailListSerializer, AddJuniorSerializer, RemoveJuniorSerializer, CompleteTaskSerializer, JuniorPointsSerializer, - AddGuardianSerializer, StartTaskSerializer, ReAssignTaskSerializer) + AddGuardianSerializer, StartTaskSerializer, ReAssignTaskSerializer + ) 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 base.constants import NUMBER, ARTICLE_STATUS from account.utils import custom_response, custom_error_response 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 web_admin.models import Article, ArticleSurvey, SurveyOption +from web_admin.models import Article, ArticleSurvey, SurveyOption, ArticleCard from web_admin.serializers.article_serializer import (ArticleSerializer, ArticleListSerializer, StartAssessmentSerializer) """ Define APIs """ @@ -479,6 +482,8 @@ class StartArticleAPIView(viewsets.ModelViewSet): 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'])) if article_data: question_query = ArticleSurvey.objects.filter(article=article_id) for question in question_query: @@ -545,3 +550,74 @@ class CheckAnswerAPIView(viewsets.ModelViewSet): 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): + """Remove junior API""" + 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): + """ junior list""" + 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} + 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): + """Remove junior API""" + permission_classes = [IsAuthenticated] + http_method_names = ('put',) + + def put(self, request, *args, **kwargs): + """ junior list""" + 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') + JuniorArticleCard.objects.filter(junior=junior_instance, + article__id=article, + article_card__id=article_card).update(is_read=True) + 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""" + permission_classes = [IsAuthenticated] + http_method_names = ('post',) + + def create(self, request, *args, **kwargs): + """ junior list""" + 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) From 9c75cb1615729c468b6bfddb92ffbaf79b821d86 Mon Sep 17 00:00:00 2001 From: abutalib-kiwi Date: Wed, 9 Aug 2023 16:57:19 +0530 Subject: [PATCH 38/61] analytics section api, users counts, new signup count, task report --- account/serializers.py | 5 +- account/views.py | 5 +- web_admin/serializers/analytics_serializer.py | 3 + web_admin/serializers/auth_serializer.py | 4 +- web_admin/urls.py | 4 + web_admin/views/analytics.py | 98 +++++++++++++++++++ web_admin/views/user_management.py | 13 ++- 7 files changed, 117 insertions(+), 15 deletions(-) create mode 100644 web_admin/serializers/analytics_serializer.py create mode 100644 web_admin/views/analytics.py diff --git a/account/serializers.py b/account/serializers.py index 5686931..4065b5c 100644 --- a/account/serializers.py +++ b/account/serializers.py @@ -154,10 +154,9 @@ class AdminLoginSerializer(serializers.ModelSerializer): user = User.objects.filter(email__iexact=attrs['email'], is_superuser=True ).only('id', 'first_name', 'last_name', 'email', '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 diff --git a/account/views.py b/account/views.py index 80530ad..c814100 100644 --- a/account/views.py +++ b/account/views.py @@ -334,10 +334,9 @@ class UserLogin(viewsets.ViewSet): user = User.objects.filter(email__iexact=email, is_superuser=True ).only('id', 'first_name', 'last_name', 'email', '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) diff --git a/web_admin/serializers/analytics_serializer.py b/web_admin/serializers/analytics_serializer.py new file mode 100644 index 0000000..5b653a6 --- /dev/null +++ b/web_admin/serializers/analytics_serializer.py @@ -0,0 +1,3 @@ +""" +web_admin analytics serializer file +""" diff --git a/web_admin/serializers/auth_serializer.py b/web_admin/serializers/auth_serializer.py index 9f603ce..712e284 100644 --- a/web_admin/serializers/auth_serializer.py +++ b/web_admin/serializers/auth_serializer.py @@ -87,9 +87,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 diff --git a/web_admin/urls.py b/web_admin/urls.py index 065d30d..d586e50 100644 --- a/web_admin/urls.py +++ b/web_admin/urls.py @@ -6,6 +6,7 @@ from django.urls import path, include from rest_framework import routers # local imports +from web_admin.views.analytics import AnalyticsViewSet from web_admin.views.article import ArticleViewSet, DefaultArticleCardImagesViewSet, ArticleListViewSet from web_admin.views.auth import ForgotAndResetPasswordViewSet from web_admin.views.user_management import UserManagementViewSet @@ -17,6 +18,9 @@ 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('article-list', ArticleListViewSet, basename='article-list') +router.register('analytics', AnalyticsViewSet, basename='user-analytics') +# router.register('task-analytics', TaskAnalyticsViewSet, basename='task-analytics') + # forgot and reset password api for admin router.register('admin', ForgotAndResetPasswordViewSet, basename='admin') diff --git a/web_admin/views/analytics.py b/web_admin/views/analytics.py new file mode 100644 index 0000000..f769c18 --- /dev/null +++ b/web_admin/views/analytics.py @@ -0,0 +1,98 @@ +""" +web_admin analytics view file +""" +import datetime + +from rest_framework.viewsets import GenericViewSet +from rest_framework.decorators import action +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 account.utils import custom_response +from base.constants import PENDING, IN_PROGRESS, REJECTED, REQUESTED, COMPLETED, EXPIRED +from guardian.models import JuniorTask + +USER = get_user_model() + + +class AnalyticsViewSet(GenericViewSet): + """ + analytics api view + """ + serializer_class = None + + 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: query params {start_date and end_date}, date format (yyyy-mm-dd) + :return: + """ + + end_date = datetime.date.today() + start_date = end_date - datetime.timedelta(days=6) + + if request.query_params.get('start_date') and request.query_params.get('end_date'): + start_date = datetime.datetime.strptime(request.query_params.get('start_date'), '%Y-%m-%d') + end_date = datetime.datetime.strptime(request.query_params.get('end_date'), '%Y-%m-%d') + + 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): + + end_date = datetime.date.today() + start_date = end_date - datetime.timedelta(days=6) + + if request.query_params.get('start_date') and request.query_params.get('end_date'): + start_date = datetime.datetime.strptime(request.query_params.get('start_date'), '%Y-%m-%d') + end_date = datetime.datetime.strptime(request.query_params.get('end_date'), '%Y-%m-%d') + + 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): + + end_date = datetime.date.today() + start_date = end_date - datetime.timedelta(days=6) + + if request.query_params.get('start_date') and request.query_params.get('end_date'): + start_date = datetime.datetime.strptime(request.query_params.get('start_date'), '%Y-%m-%d') + end_date = datetime.datetime.strptime(request.query_params.get('end_date'), '%Y-%m-%d') + + assign_tasks = JuniorTask.objects.filter( + created_at__range=[start_date, (end_date + datetime.timedelta(days=1))] + ).exclude(task_status__in=[PENDING, EXPIRED]) + + data = { + 'task_completed': assign_tasks.filter(task_status=COMPLETED).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(), + } + + return custom_response(None, data) diff --git a/web_admin/views/user_management.py b/web_admin/views/user_management.py index 3bd8f64..c16b62a 100644 --- a/web_admin/views/user_management.py +++ b/web_admin/views/user_management.py @@ -29,11 +29,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 +56,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) From 32eaa6c3f2a90d48c971f13532c553d497487bf2 Mon Sep 17 00:00:00 2001 From: jain Date: Wed, 9 Aug 2023 17:18:07 +0530 Subject: [PATCH 39/61] guardian code update --- junior/serializers.py | 3 +++ 1 file changed, 3 insertions(+) diff --git a/junior/serializers.py b/junior/serializers.py index 079031a..3575ccf 100644 --- a/junior/serializers.py +++ b/junior/serializers.py @@ -96,6 +96,9 @@ class CreateJuniorSerializer(serializers.ModelSerializer): guardian_data = Guardian.objects.filter(guardian_code=guardian_code[0]).last() if guardian_data: JuniorGuardianRelationship.objects.get_or_create(guardian=guardian_data, junior=junior) + junior.guardian_code_status = str(NUMBER['three']) + junior_approval_mail(user.email, user.first_name) + send_notification.delay(APPROVED_JUNIOR, None, guardian_data.user.id, {}) 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) From 19f56280e4a9e11d5237134c80e4f50c47716d25 Mon Sep 17 00:00:00 2001 From: jain Date: Wed, 9 Aug 2023 18:53:18 +0530 Subject: [PATCH 40/61] points remove from serializer --- junior/serializers.py | 6 +++--- web_admin/serializers/article_serializer.py | 16 +--------------- 2 files changed, 4 insertions(+), 18 deletions(-) diff --git a/junior/serializers.py b/junior/serializers.py index 3575ccf..1db1f44 100644 --- a/junior/serializers.py +++ b/junior/serializers.py @@ -96,9 +96,9 @@ class CreateJuniorSerializer(serializers.ModelSerializer): guardian_data = Guardian.objects.filter(guardian_code=guardian_code[0]).last() if guardian_data: JuniorGuardianRelationship.objects.get_or_create(guardian=guardian_data, junior=junior) - junior.guardian_code_status = str(NUMBER['three']) - junior_approval_mail(user.email, user.first_name) - send_notification.delay(APPROVED_JUNIOR, None, guardian_data.user.id, {}) + junior.guardian_code_status = str(NUMBER['three']) + junior_approval_mail(user.email, user.first_name) + send_notification.delay(APPROVED_JUNIOR, None, guardian_data.user.id, {}) 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) diff --git a/web_admin/serializers/article_serializer.py b/web_admin/serializers/article_serializer.py index 9458190..a55d4a9 100644 --- a/web_admin/serializers/article_serializer.py +++ b/web_admin/serializers/article_serializer.py @@ -58,6 +58,7 @@ class ArticleSurveySerializer(serializers.ModelSerializer): """ id = serializers.IntegerField(required=False) options = SurveyOptionSerializer(many=True) + points = serializers.IntegerField(required=False) class Meta: """ @@ -277,18 +278,3 @@ class StartAssessmentSerializer(serializers.ModelSerializer): fields = ('article_survey',) -class CheckAnswerSerializer(serializers.ModelSerializer): - """ - serializer for article API - """ - article_survey = ArticleSurveySerializer(many=True) - - class Meta: - """ - meta class - """ - model = ArticleSurvey - fields = ('id', 'article_survey') - - def create(self, validated_data): - """create function""" From af7582b9e2b085f63306caf5456c5f0022da1f77 Mon Sep 17 00:00:00 2001 From: jain Date: Wed, 9 Aug 2023 19:20:56 +0530 Subject: [PATCH 41/61] points --- web_admin/serializers/article_serializer.py | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/web_admin/serializers/article_serializer.py b/web_admin/serializers/article_serializer.py index a55d4a9..a3a7a46 100644 --- a/web_admin/serializers/article_serializer.py +++ b/web_admin/serializers/article_serializer.py @@ -58,14 +58,13 @@ class ArticleSurveySerializer(serializers.ModelSerializer): """ id = serializers.IntegerField(required=False) options = SurveyOptionSerializer(many=True) - points = serializers.IntegerField(required=False) class Meta: """ meta class """ model = ArticleSurvey - fields = ('id', 'question', 'options', 'points') + fields = ('id', 'question', 'options') class ArticleSerializer(serializers.ModelSerializer): From 22dd7fc10bfd42a03b7682b986508bb9e177fe30 Mon Sep 17 00:00:00 2001 From: jain Date: Thu, 10 Aug 2023 10:55:33 +0530 Subject: [PATCH 42/61] remove guardian code request --- base/messages.py | 4 +++- junior/serializers.py | 12 ++++++++++++ junior/urls.py | 3 ++- junior/views.py | 26 ++++++++++++++++++++++++-- 4 files changed, 41 insertions(+), 4 deletions(-) diff --git a/base/messages.py b/base/messages.py index 1e3e146..4e15d59 100644 --- a/base/messages.py +++ b/base/messages.py @@ -155,7 +155,9 @@ SUCCESS_CODE = { "3041": "Article completed successfully", # submit assessment successfully "3042": "Assessment completed successfully", - "3043": "Read article card successfully" + "3043": "Read article card successfully", + # remove guardian code request + "3044": "Remove guardian code request successfully", } """status code error""" diff --git a/junior/serializers.py b/junior/serializers.py index 1db1f44..003e563 100644 --- a/junior/serializers.py +++ b/junior/serializers.py @@ -492,3 +492,15 @@ class ReAssignTaskSerializer(serializers.ModelSerializer): 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): + instance.guardian_code_status = str(NUMBER['one']) + instance.save() + return instance diff --git a/junior/urls.py b/junior/urls.py index e494a3d..b145d4f 100644 --- a/junior/urls.py +++ b/junior/urls.py @@ -6,7 +6,7 @@ from .views import (UpdateJuniorProfile, ValidateGuardianCode, JuniorListAPIView CompleteJuniorTaskAPIView, JuniorPointsListAPIView, ValidateReferralCode, InviteGuardianAPIView, StartTaskAPIView, ReAssignJuniorTaskAPIView, StartArticleAPIView, StartAssessmentAPIView, CheckAnswerAPIView, CompleteArticleAPIView, ReadArticleCardAPIView, - CreateArticleCardAPIView) + CreateArticleCardAPIView, RemoveGuardianCodeAPIView) """Third party import""" from rest_framework import routers @@ -60,4 +60,5 @@ urlpatterns = [ 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()), ] diff --git a/junior/views.py b/junior/views.py index 5926c47..8f4e53d 100644 --- a/junior/views.py +++ b/junior/views.py @@ -33,8 +33,8 @@ from junior.models import (Junior, JuniorPoints, JuniorGuardianRelationship, Jun JuniorArticleCard) from .serializers import (CreateJuniorSerializer, JuniorDetailListSerializer, AddJuniorSerializer, RemoveJuniorSerializer, CompleteTaskSerializer, JuniorPointsSerializer, - AddGuardianSerializer, StartTaskSerializer, ReAssignTaskSerializer - ) + AddGuardianSerializer, StartTaskSerializer, ReAssignTaskSerializer, + RemoveGuardianCodeSerializer) from guardian.models import Guardian, JuniorTask from guardian.serializers import TaskDetailsSerializer, TaskDetailsjuniorSerializer from base.messages import ERROR_CODE, SUCCESS_CODE @@ -621,3 +621,25 @@ class CreateArticleCardAPIView(viewsets.ModelViewSet): 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): + """Update junior task API""" + serializer_class = RemoveGuardianCodeSerializer + permission_classes = [IsAuthenticated] + + def put(self, request, format=None): + try: + junior_queryset = Junior.objects.filter(auth=self.request.user).last() + if junior_queryset: + # use RemoveGuardianCodeSerializer serializer + serializer = RemoveGuardianCodeSerializer(junior_queryset, data=request.data, partial=True) + if serializer.is_valid(): + # save serializer + serializer.save() + 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: + return custom_error_response(str(e), response_status=status.HTTP_400_BAD_REQUEST) From b6b70af13ff909ef5a5a6f95ebe6d83f213d07fc Mon Sep 17 00:00:00 2001 From: jain Date: Thu, 10 Aug 2023 11:02:32 +0530 Subject: [PATCH 43/61] remove guardian code request --- base/messages.py | 2 ++ junior/models.py | 6 ++++-- junior/views.py | 3 ++- 3 files changed, 8 insertions(+), 3 deletions(-) diff --git a/base/messages.py b/base/messages.py index 4e15d59..c98e0e0 100644 --- a/base/messages.py +++ b/base/messages.py @@ -150,11 +150,13 @@ SUCCESS_CODE = { "3038": "Status has been changed successfully.", # notification read "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", diff --git a/junior/models.py b/junior/models.py index 180b425..12d8476 100644 --- a/junior/models.py +++ b/junior/models.py @@ -167,7 +167,8 @@ class JuniorArticle(models.Model): Survey Options model """ # associated junior with the task - junior = models.OneToOneField(Junior, on_delete=models.CASCADE, related_name='juniors_article', verbose_name='Junior') + junior = models.OneToOneField(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) @@ -184,7 +185,8 @@ 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') + 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') diff --git a/junior/views.py b/junior/views.py index 8f4e53d..b41d2aa 100644 --- a/junior/views.py +++ b/junior/views.py @@ -260,7 +260,8 @@ 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) From b30643299f341c9d190a41a8b287ebafda1197f5 Mon Sep 17 00:00:00 2001 From: jain Date: Thu, 10 Aug 2023 11:56:19 +0530 Subject: [PATCH 44/61] junior points --- guardian/serializers.py | 1 + guardian/views.py | 1 + junior/utils.py | 2 +- 3 files changed, 3 insertions(+), 1 deletion(-) diff --git a/guardian/serializers.py b/guardian/serializers.py index 12d5383..1dabc8d 100644 --- a/guardian/serializers.py +++ b/guardian/serializers.py @@ -355,6 +355,7 @@ class ApproveJuniorSerializer(serializers.ModelSerializer): instance = self.context['junior'] instance.guardian_code = [self.context['guardian_code']] instance.guardian_code_approved = True + instance.guardian_code_status = str(NUMBER['two']) instance.save() return instance diff --git a/guardian/views.py b/guardian/views.py index 8d38395..c67037c 100644 --- a/guardian/views.py +++ b/guardian/views.py @@ -311,6 +311,7 @@ class ApproveJuniorAPIView(viewsets.ViewSet): return custom_response(SUCCESS_CODE['3023'], serializer.data, response_status=status.HTTP_200_OK) else: queryset[1].guardian_code = None + queryset[1].guardian_code_status = str(NUMBER['one']) queryset[1].save() return custom_response(SUCCESS_CODE['3024'], response_status=status.HTTP_200_OK) except Exception as e: diff --git a/junior/utils.py b/junior/utils.py index 621a6dd..1afd8d5 100644 --- a/junior/utils.py +++ b/junior/utils.py @@ -50,7 +50,7 @@ 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', 'updated_at') + 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 From 656f0da89a4646ba57315d343613d646ba6d277a Mon Sep 17 00:00:00 2001 From: jain Date: Thu, 10 Aug 2023 12:06:52 +0530 Subject: [PATCH 45/61] guardian code --- account/utils.py | 3 ++- junior/serializers.py | 1 + junior/views.py | 1 + 3 files changed, 4 insertions(+), 1 deletion(-) diff --git a/account/utils.py b/account/utils.py index 4dc6acc..357f2d1 100644 --- a/account/utils.py +++ b/account/utils.py @@ -23,7 +23,7 @@ from guardian.models import Guardian from account.models import UserDelete from base.messages import ERROR_CODE from django.utils import timezone - +from base.constants import NUMBER # Define delete # user account condition, # Define delete @@ -91,6 +91,7 @@ def junior_account_update(user_tb): junior_data.is_active = False junior_data.is_verified = False junior_data.guardian_code = '{}' + junior_data.guardian_code_status = str(NUMBER['1']) junior_data.save() def guardian_account_update(user_tb): diff --git a/junior/serializers.py b/junior/serializers.py index 003e563..0f014fe 100644 --- a/junior/serializers.py +++ b/junior/serializers.py @@ -319,6 +319,7 @@ class RemoveJuniorSerializer(serializers.ModelSerializer): if instance: instance.is_invited = False instance.guardian_code = '{}' + instance.guardian_code_status = str(NUMBER['1']) instance.save() return instance diff --git a/junior/views.py b/junior/views.py index b41d2aa..471db0f 100644 --- a/junior/views.py +++ b/junior/views.py @@ -185,6 +185,7 @@ class AddJuniorAPIView(viewsets.ModelViewSet): junior = Junior.objects.filter(auth=user).first() guardian = Guardian.objects.filter(user=self.request.user).first() junior.guardian_code = [guardian.guardian_code] + junior.guardian_code_status = str(NUMBER['two']) junior.save() JuniorGuardianRelationship.objects.get_or_create(guardian=guardian, junior=junior, relationship=str(self.request.data['relationship'])) From 7a9be0326a6456ff5cec3e9ccb6a54148a44e60a Mon Sep 17 00:00:00 2001 From: jain Date: Thu, 10 Aug 2023 12:19:08 +0530 Subject: [PATCH 46/61] junior points --- junior/views.py | 8 ++------ 1 file changed, 2 insertions(+), 6 deletions(-) diff --git a/junior/views.py b/junior/views.py index 471db0f..ed42430 100644 --- a/junior/views.py +++ b/junior/views.py @@ -357,15 +357,11 @@ class JuniorPointsListAPIView(viewsets.ModelViewSet): permission_classes = [IsAuthenticated] http_method_names = ('get',) - def get_queryset(self): - """get queryset""" - update_positions_based_on_points() - return JuniorTask.objects.filter(junior__auth__email=self.request.user).last() def list(self, request, *args, **kwargs): """profile view""" - try: - queryset = self.get_queryset() + update_positions_based_on_points() + queryset = JuniorTask.objects.filter(junior__auth__email=self.request.user).last() # update position of junior serializer = JuniorPointsSerializer(queryset) return custom_response(None, serializer.data, response_status=status.HTTP_200_OK) From 69723b362f47b2353861dc7d03aa3a560860a84d Mon Sep 17 00:00:00 2001 From: jain Date: Thu, 10 Aug 2023 15:30:46 +0530 Subject: [PATCH 47/61] current page update in article api --- guardian/views.py | 4 +-- ...uniorarticle_current_card_page_and_more.py | 23 ++++++++++++ junior/models.py | 2 ++ junior/serializers.py | 2 +- junior/utils.py | 5 +-- junior/views.py | 12 +++++-- web_admin/serializers/article_serializer.py | 36 +++++++++++++++++-- web_admin/views/article.py | 8 ++--- 8 files changed, 78 insertions(+), 14 deletions(-) create mode 100644 junior/migrations/0024_juniorarticle_current_card_page_and_more.py diff --git a/guardian/views.py b/guardian/views.py index c67037c..c17c95b 100644 --- a/guardian/views.py +++ b/guardian/views.py @@ -270,14 +270,12 @@ class TopJuniorListAPIView(viewsets.ModelViewSet): """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() - - serializer = self.get_serializer(junior_total_points, many=True) + serializer = self.get_serializer(junior_total_points[:15], 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) diff --git a/junior/migrations/0024_juniorarticle_current_card_page_and_more.py b/junior/migrations/0024_juniorarticle_current_card_page_and_more.py new file mode 100644 index 0000000..b00eba9 --- /dev/null +++ b/junior/migrations/0024_juniorarticle_current_card_page_and_more.py @@ -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), + ), + ] diff --git a/junior/models.py b/junior/models.py index 12d8476..d3912e0 100644 --- a/junior/models.py +++ b/junior/models.py @@ -173,6 +173,8 @@ class JuniorArticle(models.Model): # 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) diff --git a/junior/serializers.py b/junior/serializers.py index 0f014fe..8b0f9be 100644 --- a/junior/serializers.py +++ b/junior/serializers.py @@ -146,7 +146,7 @@ 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', + 'guardian_code', 'image', 'is_invited', 'referral_code','is_active', 'is_complete_profile', 'created_at', 'image', 'updated_at'] class JuniorDetailListSerializer(serializers.ModelSerializer): diff --git a/junior/utils.py b/junior/utils.py index 1afd8d5..ba177a8 100644 --- a/junior/utils.py +++ b/junior/utils.py @@ -4,6 +4,7 @@ from django.conf import settings """Third party Django app""" from templated_email import send_templated_mail from .models import JuniorPoints +from base.constants import NUMBER from django.db.models import F # junior notification # email for sending email @@ -53,8 +54,8 @@ def update_positions_based_on_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'] diff --git a/junior/views.py b/junior/views.py index ed42430..3ffea4c 100644 --- a/junior/views.py +++ b/junior/views.py @@ -481,7 +481,8 @@ class StartArticleAPIView(viewsets.ModelViewSet): 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'])) + 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: @@ -534,6 +535,7 @@ class CheckAnswerAPIView(viewsets.ModelViewSet): 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, is_answer=True).last() junior_article_points = JuniorArticlePoints.objects.filter(junior__auth=self.request.user, @@ -541,10 +543,13 @@ class CheckAnswerAPIView(viewsets.ModelViewSet): 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) + 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) @@ -588,9 +593,12 @@ class ReadArticleCardAPIView(views.APIView): 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) diff --git a/web_admin/serializers/article_serializer.py b/web_admin/serializers/article_serializer.py index a3a7a46..e78bd3e 100644 --- a/web_admin/serializers/article_serializer.py +++ b/web_admin/serializers/article_serializer.py @@ -12,7 +12,7 @@ from base.messages import ERROR_CODE from guardian.utils import upload_image_to_alibaba 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 +from junior.models import JuniorArticlePoints, JuniorArticle USER = get_user_model() @@ -268,12 +268,44 @@ 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() + if data: + return data.current_que_page + return NUMBER['zero'] class Meta: """ meta class """ model = Article - fields = ('article_survey',) + 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: + """ + meta class + """ + model = ArticleCard + fields = ('id', 'title', 'description', 'image_name', 'image_url', 'current_page') diff --git a/web_admin/views/article.py b/web_admin/views/article.py index d4cc843..e57577e 100644 --- a/web_admin/views/article.py +++ b/web_admin/views/article.py @@ -16,8 +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) USER = get_user_model() @@ -229,7 +229,7 @@ class ArticleListViewSet(GenericViewSet, mixins.ListModelMixin): class ArticleCardListViewSet(viewsets.ModelViewSet): """Junior Points viewset""" - serializer_class = ArticleCardSerializer + serializer_class = ArticleCardlistSerializer permission_classes = [IsAuthenticated] http_method_names = ('get',) @@ -242,7 +242,7 @@ class ArticleCardListViewSet(viewsets.ModelViewSet): try: queryset = self.get_queryset() # article card list - serializer = ArticleCardSerializer(queryset, many=True) + 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) From 22afe7e5559eb4848b8f0c9652b8c75d0467052c Mon Sep 17 00:00:00 2001 From: jain Date: Thu, 10 Aug 2023 15:43:31 +0530 Subject: [PATCH 48/61] current page update in article api --- guardian/views.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/guardian/views.py b/guardian/views.py index c17c95b..2194f4b 100644 --- a/guardian/views.py +++ b/guardian/views.py @@ -275,7 +275,7 @@ class TopJuniorListAPIView(viewsets.ModelViewSet): junior.position = index + 1 send_notification.delay(LEADERBOARD_RANKING, None, junior.junior.auth.id, {}) junior.save() - serializer = self.get_serializer(junior_total_points[:15], many=True) + serializer = self.get_serializer(junior_total_points[:NUMBER['fifteen']], 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) From 0a8c2cf56dd02aad0d05189a5abd53f6683cd464 Mon Sep 17 00:00:00 2001 From: abutalib-kiwi Date: Thu, 10 Aug 2023 18:17:49 +0530 Subject: [PATCH 49/61] image name fix, remove '|' character --- web_admin/utils.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/web_admin/utils.py b/web_admin/utils.py index 6cdc240..9870b30 100644 --- a/web_admin/utils.py +++ b/web_admin/utils.py @@ -29,7 +29,7 @@ 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 = f"{data['title']} {data.pop('image_name')}" if 'image_name' in data else data['title'] filename = f"{ARTICLE_CARD_IMAGE_FOLDER}/{image_name}" # upload image on ali baba image_url = upload_image_to_alibaba(base64_image, filename) From 11c84208b753f8ef536bcaa0efbf55fbb450f503 Mon Sep 17 00:00:00 2001 From: jain Date: Thu, 10 Aug 2023 19:00:37 +0530 Subject: [PATCH 50/61] task points --- junior/serializers.py | 8 +++++--- 1 file changed, 5 insertions(+), 3 deletions(-) diff --git a/junior/serializers.py b/junior/serializers.py index 8b0f9be..a6eff8d 100644 --- a/junior/serializers.py +++ b/junior/serializers.py @@ -184,8 +184,10 @@ class JuniorDetailListSerializer(serializers.ModelSerializer): 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 + data = JuniorPoints.objects.filter(junior=obj).last() + if 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() @@ -210,7 +212,7 @@ class JuniorDetailListSerializer(serializers.ModelSerializer): 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', 'guardian_code_status'] From fbd6f9ece52d456c3bc9d41a4d7be26395c1d6b2 Mon Sep 17 00:00:00 2001 From: jain Date: Thu, 10 Aug 2023 19:05:33 +0530 Subject: [PATCH 51/61] task points --- junior/serializers.py | 1 - 1 file changed, 1 deletion(-) diff --git a/junior/serializers.py b/junior/serializers.py index a6eff8d..57a821b 100644 --- a/junior/serializers.py +++ b/junior/serializers.py @@ -182,7 +182,6 @@ class JuniorDetailListSerializer(serializers.ModelSerializer): data = JuniorPoints.objects.filter(junior=obj).last() if data: return data.position - return 99999 def get_points(self, obj): data = JuniorPoints.objects.filter(junior=obj).last() if data: From 1e162255d7d3b60da0ecb6250df686367e970461 Mon Sep 17 00:00:00 2001 From: jain Date: Thu, 10 Aug 2023 20:17:24 +0530 Subject: [PATCH 52/61] create article --- .../0025_alter_juniorarticle_junior.py | 19 +++++++++++++++++++ junior/models.py | 2 +- 2 files changed, 20 insertions(+), 1 deletion(-) create mode 100644 junior/migrations/0025_alter_juniorarticle_junior.py diff --git a/junior/migrations/0025_alter_juniorarticle_junior.py b/junior/migrations/0025_alter_juniorarticle_junior.py new file mode 100644 index 0000000..26c7966 --- /dev/null +++ b/junior/migrations/0025_alter_juniorarticle_junior.py @@ -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'), + ), + ] diff --git a/junior/models.py b/junior/models.py index d3912e0..025843e 100644 --- a/junior/models.py +++ b/junior/models.py @@ -167,7 +167,7 @@ class JuniorArticle(models.Model): Survey Options model """ # associated junior with the task - junior = models.OneToOneField(Junior, on_delete=models.CASCADE, related_name='juniors_article', + 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""" From bb65f81200acfdda3453fbdaca0683886577e9c4 Mon Sep 17 00:00:00 2001 From: jain Date: Fri, 11 Aug 2023 11:48:02 +0530 Subject: [PATCH 53/61] add correct and attempt answer --- web_admin/serializers/article_serializer.py | 21 ++++++++++++++++++++- 1 file changed, 20 insertions(+), 1 deletion(-) diff --git a/web_admin/serializers/article_serializer.py b/web_admin/serializers/article_serializer.py index e78bd3e..29230eb 100644 --- a/web_admin/serializers/article_serializer.py +++ b/web_admin/serializers/article_serializer.py @@ -247,6 +247,9 @@ class ArticleQuestionSerializer(serializers.ModelSerializer): 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""" @@ -256,12 +259,28 @@ class ArticleQuestionSerializer(serializers.ModelSerializer): 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 str("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, is_answer_correct=True).last() + if junior_article_obj: + return junior_article_obj.submitted_answer.id + return None + class Meta: """ meta class """ model = ArticleSurvey - fields = ('id', 'question', 'options', 'points', 'is_attempt') + fields = ('id', 'question', 'options', 'points', 'is_attempt', 'correct_answer', 'attempted_answer') class StartAssessmentSerializer(serializers.ModelSerializer): """ From 8b0032f6d25b0c2dab51bd6f0166937392677ff1 Mon Sep 17 00:00:00 2001 From: jain Date: Fri, 11 Aug 2023 12:18:35 +0530 Subject: [PATCH 54/61] junior point table --- junior/views.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/junior/views.py b/junior/views.py index 3ffea4c..a86083d 100644 --- a/junior/views.py +++ b/junior/views.py @@ -361,7 +361,7 @@ class JuniorPointsListAPIView(viewsets.ModelViewSet): """profile view""" try: update_positions_based_on_points() - queryset = JuniorTask.objects.filter(junior__auth__email=self.request.user).last() + queryset = JuniorPoints.objects.filter(junior__auth__email=self.request.user).last() # update position of junior serializer = JuniorPointsSerializer(queryset) return custom_response(None, serializer.data, response_status=status.HTTP_200_OK) From 5eab8f49079ff3f4265b23501e8ccf1e48f63447 Mon Sep 17 00:00:00 2001 From: jain Date: Fri, 11 Aug 2023 13:04:05 +0530 Subject: [PATCH 55/61] is complete article key --- web_admin/serializers/article_serializer.py | 3 +++ 1 file changed, 3 insertions(+) diff --git a/web_admin/serializers/article_serializer.py b/web_admin/serializers/article_serializer.py index 29230eb..c58f97a 100644 --- a/web_admin/serializers/article_serializer.py +++ b/web_admin/serializers/article_serializer.py @@ -238,6 +238,9 @@ class ArticleListSerializer(serializers.ModelSerializer): def get_is_completed(self, obj): """complete all question""" + junior_article = JuniorArticle.objects.filter(article=obj).last() + if junior_article: + junior_article.is_completed return False class ArticleQuestionSerializer(serializers.ModelSerializer): From af8ea39200ac498481731b57e02322a7ea552070 Mon Sep 17 00:00:00 2001 From: abutalib-kiwi Date: Fri, 11 Aug 2023 13:35:12 +0530 Subject: [PATCH 56/61] leaderboard ranking --- celerybeat-schedule | Bin 16384 -> 16384 bytes web_admin/serializers/analytics_serializer.py | 50 ++++++++++++++++++ web_admin/views/analytics.py | 36 +++++++++++-- web_admin/views/user_management.py | 2 +- zod_bank/settings.py | 25 +++++++++ 5 files changed, 109 insertions(+), 4 deletions(-) diff --git a/celerybeat-schedule b/celerybeat-schedule index 4a51b891af30ec5d0a7f9b91777d2f6b8b1a45d1..f2510fc7dfb09da6514820cff292233776337c68 100644 GIT binary patch delta 33 pcmZo@U~Fh$+_2GzgM*t@UXD?{c=Bz7Kt|Tdx`vvYO^na*005(w36B5( delta 33 pcmZo@U~Fh$+_2GzgI$KjRG8~~^yJ$Hfs9O(bqzH)n;4(r0RXa_3TFTS diff --git a/web_admin/serializers/analytics_serializer.py b/web_admin/serializers/analytics_serializer.py index 5b653a6..34316b7 100644 --- a/web_admin/serializers/analytics_serializer.py +++ b/web_admin/serializers/analytics_serializer.py @@ -1,3 +1,53 @@ """ web_admin analytics serializer file """ +from rest_framework import serializers + +from junior.models import JuniorPoints, Junior +from web_admin.serializers.user_management_serializer import JuniorSerializer + + +class JuniorLeaderboardSerializer(serializers.ModelSerializer): + 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') + + @staticmethod + def get_name(obj): + """ + :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 + + @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): + junior = JuniorLeaderboardSerializer() + rank = serializers.IntegerField() + + class Meta: + model = JuniorPoints + fields = ('total_points', 'rank', 'junior') diff --git a/web_admin/views/analytics.py b/web_admin/views/analytics.py index f769c18..688e445 100644 --- a/web_admin/views/analytics.py +++ b/web_admin/views/analytics.py @@ -7,12 +7,16 @@ from rest_framework.viewsets import GenericViewSet from rest_framework.decorators import action from django.contrib.auth import get_user_model from django.db.models import Q -from django.db.models import Count +from django.db.models import Count, OuterRef, Subquery, Sum from django.db.models.functions import TruncDate +from django.db.models import F, Window +from django.db.models.functions.window import Rank from account.utils import custom_response from base.constants import PENDING, IN_PROGRESS, REJECTED, REQUESTED, COMPLETED, EXPIRED from guardian.models import JuniorTask +from junior.models import JuniorPoints +from web_admin.serializers.analytics_serializer import LeaderboardSerializer USER = get_user_model() @@ -59,7 +63,11 @@ class AnalyticsViewSet(GenericViewSet): @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: query params {start_date and end_date}, date format (yyyy-mm-dd) + :return: + """ end_date = datetime.date.today() start_date = end_date - datetime.timedelta(days=6) @@ -76,7 +84,11 @@ class AnalyticsViewSet(GenericViewSet): @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 + :param request: query params {start_date and end_date}, date format (yyyy-mm-dd) + :return: + """ end_date = datetime.date.today() start_date = end_date - datetime.timedelta(days=6) @@ -96,3 +108,21 @@ class AnalyticsViewSet(GenericViewSet): } 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): + """ + + :param request: + :return: + """ + # queryset = JuniorPoints.objects.all().order_by('-total_points', 'junior__created_at') + queryset = JuniorPoints.objects.prefetch_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 = self.pagination_class() + paginated_queryset = paginator.paginate_queryset(queryset, request) + serializer = self.serializer_class(paginated_queryset, many=True) + return custom_response(None, serializer.data) diff --git a/web_admin/views/user_management.py b/web_admin/views/user_management.py index c16b62a..8f53a73 100644 --- a/web_admin/views/user_management.py +++ b/web_admin/views/user_management.py @@ -34,7 +34,7 @@ class UserManagementViewSet(GenericViewSet, mixins.ListModelMixin, is_superuser=False).prefetch_related('guardian_profile', 'junior_profile' ).exclude(junior_profile__isnull=True, - guardian_profile__isnull=True).order_by('date_joined') + guardian_profile__isnull=True).order_by('-date_joined') filter_backends = (SearchFilter,) search_fields = ['first_name', 'last_name'] http_method_names = ['get', 'post', 'patch'] diff --git a/zod_bank/settings.py b/zod_bank/settings.py index 3781f89..2d003c1 100644 --- a/zod_bank/settings.py +++ b/zod_bank/settings.py @@ -177,6 +177,31 @@ AUTH_PASSWORD_VALIDATORS = [ }, ] +LOGGING = { + "version": 1, + "filters": { + "require_debug_true": { + "()": "django.utils.log.RequireDebugTrue" + } + }, + "handlers": { + "console": { + "level": "DEBUG", + "filters": [ + "require_debug_true" + ], + "class": "logging.StreamHandler" + } + }, + "loggers": { + "django.db.backends": { + "level": "DEBUG", + "handlers": [ + "console" + ] + } + } +} # Internationalization # https://docs.djangoproject.com/en/3.0/topics/i18n/ From 94c76b7f2e9c907082650f0715acefe296fd3f0c Mon Sep 17 00:00:00 2001 From: jain Date: Fri, 11 Aug 2023 13:39:25 +0530 Subject: [PATCH 57/61] remove deleted user from leaderboard --- account/utils.py | 4 +++- account/views.py | 6 +++++- 2 files changed, 8 insertions(+), 2 deletions(-) diff --git a/account/utils.py b/account/utils.py index 357f2d1..d33b267 100644 --- a/account/utils.py +++ b/account/utils.py @@ -24,6 +24,7 @@ from account.models import UserDelete 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 @@ -91,8 +92,9 @@ def junior_account_update(user_tb): junior_data.is_active = False junior_data.is_verified = False junior_data.guardian_code = '{}' - junior_data.guardian_code_status = str(NUMBER['1']) + junior_data.guardian_code_status = str(NUMBER['one']) junior_data.save() + JuniorPoints.objects.filter(junior=junior_data).delete() def guardian_account_update(user_tb): """update guardian account after delete the user account""" diff --git a/account/views.py b/account/views.py index 0f9747e..593be1c 100644 --- a/account/views.py +++ b/account/views.py @@ -23,7 +23,7 @@ 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 django.contrib.auth.models import User @@ -97,6 +97,8 @@ class GoogleLoginMixin(object): referral_code=generate_code(ZOD, user_obj.id) ) serializer = JuniorSerializer(junior_query) + position = Junior.objects.all().count() + JuniorPoints.objects.create(junior=junior_query, position=position) if str(user_type) == '2': guardian_query = Guardian.objects.create(user=user_obj, is_verified=True, is_active=True, image=profile_picture,signup_method='2', @@ -147,6 +149,8 @@ class SigninWithApple(views.APIView): junior_code=generate_code(JUN, user.id), referral_code=generate_code(ZOD, user.id)) serializer = JuniorSerializer(junior_query) + position = Junior.objects.all().count() + JuniorPoints.objects.create(junior=junior_query, position=position) if str(user_type) == '2': guardian_query = Guardian.objects.create(user=user, is_verified=True, is_active=True, signup_method='3', From 69c19cf09714846ff70232106ab6173b2002ce6a Mon Sep 17 00:00:00 2001 From: abutalib-kiwi Date: Fri, 11 Aug 2023 14:40:49 +0530 Subject: [PATCH 58/61] sonar issues --- account/serializers.py | 3 +- account/views.py | 3 +- base/constants.py | 2 ++ guardian/views.py | 32 ----------------- junior/serializers.py | 1 - web_admin/serializers/analytics_serializer.py | 1 - web_admin/views/analytics.py | 34 +++++++++++-------- 7 files changed, 26 insertions(+), 50 deletions(-) diff --git a/account/serializers.py b/account/serializers.py index 4065b5c..edf4de9 100644 --- a/account/serializers.py +++ b/account/serializers.py @@ -152,7 +152,8 @@ 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 or not user.check_password(attrs['password']): raise serializers.ValidationError({'details': ERROR_CODE['2002']}) diff --git a/account/views.py b/account/views.py index fca583b..8c2932d 100644 --- a/account/views.py +++ b/account/views.py @@ -334,7 +334,8 @@ class UserLogin(viewsets.ViewSet): 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 or not user.check_password(password): return custom_error_response(ERROR_CODE["2002"], response_status=status.HTTP_400_BAD_REQUEST) diff --git a/base/constants.py b/base/constants.py index 36079b9..05fa8b8 100644 --- a/base/constants.py +++ b/base/constants.py @@ -122,3 +122,5 @@ MAX_ARTICLE_SURVEY = 10 Already_register_user = "duplicate key value violates unique constraint" ARTICLE_CARD_IMAGE_FOLDER = 'article-card-images' + +DATE_FORMAT = '%Y-%m-%d' diff --git a/guardian/views.py b/guardian/views.py index 2194f4b..3948e6f 100644 --- a/guardian/views.py +++ b/guardian/views.py @@ -124,38 +124,6 @@ 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""" diff --git a/junior/serializers.py b/junior/serializers.py index 57a821b..59fbe07 100644 --- a/junior/serializers.py +++ b/junior/serializers.py @@ -333,7 +333,6 @@ 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 diff --git a/web_admin/serializers/analytics_serializer.py b/web_admin/serializers/analytics_serializer.py index 34316b7..fd85118 100644 --- a/web_admin/serializers/analytics_serializer.py +++ b/web_admin/serializers/analytics_serializer.py @@ -4,7 +4,6 @@ web_admin analytics serializer file from rest_framework import serializers from junior.models import JuniorPoints, Junior -from web_admin.serializers.user_management_serializer import JuniorSerializer class JuniorLeaderboardSerializer(serializers.ModelSerializer): diff --git a/web_admin/views/analytics.py b/web_admin/views/analytics.py index 688e445..0133339 100644 --- a/web_admin/views/analytics.py +++ b/web_admin/views/analytics.py @@ -7,13 +7,13 @@ from rest_framework.viewsets import GenericViewSet from rest_framework.decorators import action from django.contrib.auth import get_user_model from django.db.models import Q -from django.db.models import Count, OuterRef, Subquery, Sum +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 account.utils import custom_response -from base.constants import PENDING, IN_PROGRESS, REJECTED, REQUESTED, COMPLETED, EXPIRED +from base.constants import PENDING, IN_PROGRESS, REJECTED, REQUESTED, COMPLETED, EXPIRED, DATE_FORMAT from guardian.models import JuniorTask from junior.models import JuniorPoints from web_admin.serializers.analytics_serializer import LeaderboardSerializer @@ -24,6 +24,10 @@ 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 @@ -41,7 +45,8 @@ class AnalyticsViewSet(GenericViewSet): def total_users_count(self, request, *args, **kwargs): """ api method to get total users, guardians and juniors - :param request: query params {start_date and end_date}, date format (yyyy-mm-dd) + :param request: start_date: date format (yyyy-mm-dd) + :param request: end_date: date format (yyyy-mm-dd) :return: """ @@ -49,8 +54,8 @@ class AnalyticsViewSet(GenericViewSet): start_date = end_date - datetime.timedelta(days=6) if request.query_params.get('start_date') and request.query_params.get('end_date'): - start_date = datetime.datetime.strptime(request.query_params.get('start_date'), '%Y-%m-%d') - end_date = datetime.datetime.strptime(request.query_params.get('end_date'), '%Y-%m-%d') + start_date = datetime.datetime.strptime(request.query_params.get('start_date'), DATE_FORMAT) + end_date = datetime.datetime.strptime(request.query_params.get('end_date'), DATE_FORMAT) user_qs = self.get_queryset() queryset = user_qs.filter(date_joined__range=(start_date, (end_date + datetime.timedelta(days=1)))) @@ -65,15 +70,16 @@ class AnalyticsViewSet(GenericViewSet): def new_signups(self, request, *args, **kwargs): """ api method to get new signups - :param request: query params {start_date and end_date}, date format (yyyy-mm-dd) + :param request: start_date: date format (yyyy-mm-dd) + :param request: end_date: date format (yyyy-mm-dd) :return: """ end_date = datetime.date.today() start_date = end_date - datetime.timedelta(days=6) if request.query_params.get('start_date') and request.query_params.get('end_date'): - start_date = datetime.datetime.strptime(request.query_params.get('start_date'), '%Y-%m-%d') - end_date = datetime.datetime.strptime(request.query_params.get('end_date'), '%Y-%m-%d') + start_date = datetime.datetime.strptime(request.query_params.get('start_date'), DATE_FORMAT) + end_date = datetime.datetime.strptime(request.query_params.get('end_date'), DATE_FORMAT) user_qs = self.get_queryset() signup_data = user_qs.filter(date_joined__range=[start_date, (end_date + datetime.timedelta(days=1))] @@ -85,16 +91,17 @@ class AnalyticsViewSet(GenericViewSet): @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 - :param request: query params {start_date and end_date}, date format (yyyy-mm-dd) + 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: """ end_date = datetime.date.today() start_date = end_date - datetime.timedelta(days=6) if request.query_params.get('start_date') and request.query_params.get('end_date'): - start_date = datetime.datetime.strptime(request.query_params.get('start_date'), '%Y-%m-%d') - end_date = datetime.datetime.strptime(request.query_params.get('end_date'), '%Y-%m-%d') + start_date = datetime.datetime.strptime(request.query_params.get('start_date'), DATE_FORMAT) + end_date = datetime.datetime.strptime(request.query_params.get('end_date'), DATE_FORMAT) assign_tasks = JuniorTask.objects.filter( created_at__range=[start_date, (end_date + datetime.timedelta(days=1))] @@ -113,11 +120,10 @@ class AnalyticsViewSet(GenericViewSet): serializer_class=LeaderboardSerializer) def junior_leaderboard(self, request): """ - + to get junior leaderboard and rank :param request: :return: """ - # queryset = JuniorPoints.objects.all().order_by('-total_points', 'junior__created_at') queryset = JuniorPoints.objects.prefetch_related('junior', 'junior__auth').annotate(rank=Window( expression=Rank(), order_by=[F('total_points').desc(), 'junior__created_at'] From 54200dba5276990c9771e05d8200a7de28b38f8b Mon Sep 17 00:00:00 2001 From: jain Date: Fri, 11 Aug 2023 15:39:25 +0530 Subject: [PATCH 59/61] is complete article key --- web_admin/serializers/article_serializer.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/web_admin/serializers/article_serializer.py b/web_admin/serializers/article_serializer.py index c58f97a..f8c7f67 100644 --- a/web_admin/serializers/article_serializer.py +++ b/web_admin/serializers/article_serializer.py @@ -240,7 +240,7 @@ class ArticleListSerializer(serializers.ModelSerializer): """complete all question""" junior_article = JuniorArticle.objects.filter(article=obj).last() if junior_article: - junior_article.is_completed + return junior_article.is_completed return False class ArticleQuestionSerializer(serializers.ModelSerializer): From d5c30f2029d6ed4e6529c1e81f1fd8104c9ccac3 Mon Sep 17 00:00:00 2001 From: abutalib-kiwi Date: Fri, 11 Aug 2023 15:54:54 +0530 Subject: [PATCH 60/61] added pagination in leaderboard admin --- web_admin/pagination.py | 13 +++++++++++++ web_admin/views/analytics.py | 11 ++++++++++- 2 files changed, 23 insertions(+), 1 deletion(-) create mode 100644 web_admin/pagination.py diff --git a/web_admin/pagination.py b/web_admin/pagination.py new file mode 100644 index 0000000..6a6aff4 --- /dev/null +++ b/web_admin/pagination.py @@ -0,0 +1,13 @@ +""" +web_admin pagination file +""" +from rest_framework.pagination import PageNumberPagination + + +class CustomPageNumberPagination(PageNumberPagination): + """ + custom paginator class + """ + page_size = 10 # Set the desired page size + page_size_query_param = 'page_size' + max_page_size = 100 # Set a maximum page size if needed diff --git a/web_admin/views/analytics.py b/web_admin/views/analytics.py index 688e445..e8a8f5c 100644 --- a/web_admin/views/analytics.py +++ b/web_admin/views/analytics.py @@ -1,10 +1,15 @@ """ web_admin analytics view file """ +# python imports import datetime +# 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, OuterRef, Subquery, Sum @@ -12,10 +17,13 @@ from django.db.models.functions import TruncDate from django.db.models import F, Window from django.db.models.functions.window import Rank +# local imports from account.utils import custom_response from base.constants import PENDING, IN_PROGRESS, REJECTED, REQUESTED, COMPLETED, EXPIRED from guardian.models import JuniorTask from junior.models import JuniorPoints +from web_admin.pagination import CustomPageNumberPagination +from web_admin.permission import AdminPermission from web_admin.serializers.analytics_serializer import LeaderboardSerializer USER = get_user_model() @@ -26,6 +34,7 @@ class AnalyticsViewSet(GenericViewSet): analytics api view """ serializer_class = None + permission_classes = [IsAuthenticated, AdminPermission] def get_queryset(self): user_qs = USER.objects.filter( @@ -122,7 +131,7 @@ class AnalyticsViewSet(GenericViewSet): expression=Rank(), order_by=[F('total_points').desc(), 'junior__created_at'] )).order_by('-total_points', 'junior__created_at') - paginator = self.pagination_class() + paginator = CustomPageNumberPagination() paginated_queryset = paginator.paginate_queryset(queryset, request) serializer = self.serializer_class(paginated_queryset, many=True) return custom_response(None, serializer.data) From 082f93ff9d135d39d0f868d202cb231699c168e0 Mon Sep 17 00:00:00 2001 From: jain Date: Fri, 11 Aug 2023 16:54:27 +0530 Subject: [PATCH 61/61] guardian code chnages --- junior/serializers.py | 1 + 1 file changed, 1 insertion(+) diff --git a/junior/serializers.py b/junior/serializers.py index 57a821b..3101886 100644 --- a/junior/serializers.py +++ b/junior/serializers.py @@ -503,6 +503,7 @@ class RemoveGuardianCodeSerializer(serializers.ModelSerializer): model = Junior fields = ('id', ) def update(self, instance, validated_data): + instance.guardian_code = None instance.guardian_code_status = str(NUMBER['one']) instance.save() return instance