diff --git a/account/utils.py b/account/utils.py index 9122477..428df15 100644 --- a/account/utils.py +++ b/account/utils.py @@ -289,7 +289,13 @@ def get_user_full_name(user_obj): """ return f"{user_obj.first_name} {user_obj.last_name}" if user_obj.first_name or user_obj.last_name else "User" + def make_special_password(length=10): + """ + to make secured password + :param length: + :return: + """ # Define character sets lowercase_letters = string.ascii_lowercase uppercase_letters = string.ascii_uppercase @@ -301,11 +307,11 @@ def make_special_password(length=10): # Create a password with random characters password = ( - random.choice(lowercase_letters) + - random.choice(uppercase_letters) + - random.choice(digits) + - random.choice(special_characters) + - ''.join(random.choice(all_characters) for _ in range(length - 4)) + secrets.choice(lowercase_letters) + + secrets.choice(uppercase_letters) + + secrets.choice(digits) + + secrets.choice(special_characters) + + ''.join(secrets.choice(all_characters) for _ in range(length - 4)) ) # Shuffle the characters to make it more random diff --git a/base/messages.py b/base/messages.py index 0af15a1..e297137 100644 --- a/base/messages.py +++ b/base/messages.py @@ -125,11 +125,11 @@ SUCCESS_CODE = { # Success code for Thank you "3002": "Thank you for contacting us! Our Consumer Experience Team will reach out to you shortly.", # Success code for account activation - "3003": "Log in successful", + "3003": "Log in successful.", # Success code for password reset - "3004": "Password reset link has been sent to your email address", + "3004": "Password reset link has been sent to your email address.", # Success code for link verified - "3005": "Your account is deleted successfully.", + "3005": "Your account has been deleted successfully.", # Success code for password reset "3006": "Password reset successful. You can now log in with your new password.", # Success code for password update @@ -137,11 +137,11 @@ SUCCESS_CODE = { # Success code for valid link "3008": "You have a valid link.", # Success code for logged out - "3009": "You have successfully logged out!", + "3009": "You have successfully logged out.", # Success code for check all fields - "3010": "All fields are valid", - "3011": "Email OTP Verified successfully", - "3012": "Phone OTP Verified successfully", + "3010": "All fields are valid.", + "3011": "Email OTP has been verified successfully.", + "3012": "Phone OTP has been verified successfully.", "3013": "Valid Guardian code", "3014": "Password has been updated successfully.", "3015": "Verification code has been sent on your email.", @@ -150,39 +150,39 @@ SUCCESS_CODE = { "3018": "Task created successfully", "3019": "Support Email sent successfully", "3020": "Logged out successfully.", - "3021": "Added junior successfully", - "3022": "Removed junior successfully", - "3023": "Junior is approved successfully", - "3024": "Junior request is rejected successfully", - "3025": "Task is approved successfully", - "3026": "Task is rejected successfully", + "3021": "Junior has been added successfully.", + "3022": "Junior has been removed successfully.", + "3023": "Junior has been approved successfully.", + "3024": "Junior request is rejected successfully.", + "3025": "Task is approved successfully.", + "3026": "Task is rejected successfully.", "3027": "Article has been created successfully.", "3028": "Article has been updated successfully.", "3029": "Article has been deleted successfully.", "3030": "Article Card has been removed successfully.", "3031": "Article Survey has been removed successfully.", - "3032": "Task request sent successfully", - "3033": "Valid Referral code", - "3034": "Invite guardian successfully", - "3035": "Task started successfully", - "3036": "Task reassign successfully", + "3032": "Task request sent successfully.", + "3033": "Valid Referral code.", + "3034": "Invite guardian successfully.", + "3035": "Task started successfully.", + "3036": "Task reassign successfully.", "3037": "Profile has been updated successfully.", "3038": "Status has been changed successfully.", # notification read - "3039": "Notification read successfully", + "3039": "Notification read successfully.", # start article - "3040": "Start article successfully", + "3040": "Start article successfully.", # complete article - "3041": "Article completed successfully", + "3041": "Article completed successfully.", # submit assessment successfully - "3042": "Assessment completed successfully", + "3042": "Assessment completed successfully.", # read article - "3043": "Read article card successfully", + "3043": "Read article card successfully.", # remove guardian code request - "3044": "Remove guardian code request successfully", + "3044": "Remove guardian code request successfully.", # create faq - "3045": "Create FAQ data", - "3046": "Add App version successfully" + "3045": "Create FAQ data.", + "3046": "Add App version successfully." } """status code error""" diff --git a/guardian/views.py b/guardian/views.py index 4a8a804..45ba54d 100644 --- a/guardian/views.py +++ b/guardian/views.py @@ -185,9 +185,12 @@ class CreateTaskAPIView(viewsets.ModelViewSet): try: image = request.data['default_image'] junior_ids = request.data['junior'].split(',') - # if not junior.isnumeric(): - # """junior value must be integer""" - # return custom_error_response(ERROR_CODE['2047'], response_status=status.HTTP_400_BAD_REQUEST) + + invalid_junior_ids = [junior_id for junior_id in junior_ids if not junior_id.isnumeric()] + if invalid_junior_ids: + # At least one junior value is not an integer + return custom_error_response(ERROR_CODE['2047'], response_status=status.HTTP_400_BAD_REQUEST) + allowed_extensions = ['.jpg', '.jpeg', '.png'] if not any(extension in str(image) for extension in allowed_extensions): return custom_error_response(ERROR_CODE['2048'], response_status=status.HTTP_400_BAD_REQUEST) @@ -202,16 +205,17 @@ class CreateTaskAPIView(viewsets.ModelViewSet): request.data.pop('default_image') guardian = Guardian.objects.filter(user=request.user).select_related('user').last() - junior_data = Junior.objects.filter(id__in=junior_ids).select_related('auth') - - for junior in junior_data: - if junior: + junior_data = Junior.objects.filter(id__in=junior_ids, + guardian_code__contains=[guardian.guardian_code] + ).select_related('auth') + if junior_data: + for junior in junior_data: index = junior.guardian_code.index(guardian.guardian_code) status_index = junior.guardian_code_status[index] if status_index == str(NUMBER['three']): return custom_error_response(ERROR_CODE['2078'], response_status=status.HTTP_400_BAD_REQUEST) - else: - return custom_error_response(ERROR_CODE['2047'], response_status=status.HTTP_400_BAD_REQUEST) + else: + return custom_error_response(ERROR_CODE['2047'], response_status=status.HTTP_400_BAD_REQUEST) # use TaskSerializer serializer serializer = TaskSerializer(context={"guardian": guardian, "image": image_data, diff --git a/web_admin/serializers/article_serializer.py b/web_admin/serializers/article_serializer.py index d030d41..c1bde0e 100644 --- a/web_admin/serializers/article_serializer.py +++ b/web_admin/serializers/article_serializer.py @@ -15,6 +15,7 @@ from notifications.utils import send_notification_multiple_user from web_admin.models import Article, ArticleCard, SurveyOption, ArticleSurvey, DefaultArticleCardImage from web_admin.utils import pop_id, get_image_url from junior.models import JuniorArticlePoints, JuniorArticle + USER = get_user_model() @@ -90,10 +91,9 @@ class ArticleSerializer(serializers.ModelSerializer): """ article_cards = attrs.get('article_cards', None) article_survey = attrs.get('article_survey', None) - if article_cards is None or len(article_cards) > int(MAX_ARTICLE_CARD): + if not 0 < len(article_cards) <= int(MAX_ARTICLE_CARD): raise serializers.ValidationError({'details': ERROR_CODE['2039']}) - if article_survey is None or len(article_survey) < int(MIN_ARTICLE_SURVEY) or int( - MAX_ARTICLE_SURVEY) < len(article_survey): + if not int(MIN_ARTICLE_SURVEY) <= len(article_survey) <= int(MAX_ARTICLE_SURVEY): raise serializers.ValidationError({'details': ERROR_CODE['2040']}) return attrs @@ -185,6 +185,28 @@ class ArticleSerializer(serializers.ModelSerializer): return instance +class ArticleStatusChangeSerializer(serializers.ModelSerializer): + """ + Article status change serializer + """ + class Meta: + """ + meta class + """ + model = Article + fields = ('is_published', ) + + def update(self, instance, validated_data): + """ + :param instance: article object + :param validated_data: + :return: + """ + instance.is_published = validated_data['is_published'] + instance.save() + return instance + + class DefaultArticleCardImageSerializer(serializers.ModelSerializer): """ Article Card serializer @@ -221,6 +243,7 @@ class DefaultArticleCardImageSerializer(serializers.ModelSerializer): card_image = DefaultArticleCardImage.objects.create(**validated_data) return card_image + class ArticleListSerializer(serializers.ModelSerializer): """ serializer for article API @@ -234,13 +257,14 @@ class ArticleListSerializer(serializers.ModelSerializer): meta class """ model = Article - fields = ('id', 'title', 'description','image', 'total_points', 'is_completed') + fields = ('id', 'title', 'description', 'image', 'total_points', 'is_completed') def get_image(self, obj): """article image""" if obj.article_cards.first(): return obj.article_cards.first().image_url return None + def get_total_points(self, obj): """total points of article""" return obj.article_survey.all().count() * NUMBER['five'] @@ -253,6 +277,7 @@ class ArticleListSerializer(serializers.ModelSerializer): return junior_article.is_completed return False + class ArticleQuestionSerializer(serializers.ModelSerializer): """ article survey serializer @@ -263,7 +288,6 @@ class ArticleQuestionSerializer(serializers.ModelSerializer): correct_answer = serializers.SerializerMethodField('get_correct_answer') attempted_answer = serializers.SerializerMethodField('get_attempted_answer') - def get_is_attempt(self, obj): """attempt question or not""" context_data = self.context.get('user') @@ -295,6 +319,7 @@ class ArticleQuestionSerializer(serializers.ModelSerializer): model = ArticleSurvey fields = ('id', 'question', 'options', 'points', 'is_attempt', 'correct_answer', 'attempted_answer') + class StartAssessmentSerializer(serializers.ModelSerializer): """ serializer for article API @@ -310,6 +335,7 @@ class StartAssessmentSerializer(serializers.ModelSerializer): if data: return data.current_que_page if data.current_que_page < total_count else data.current_que_page - 1 return NUMBER['zero'] + class Meta(object): """ meta class @@ -318,7 +344,6 @@ class StartAssessmentSerializer(serializers.ModelSerializer): fields = ('article_survey', 'current_page') - class ArticleCardlistSerializer(serializers.ModelSerializer): """ Article Card serializer diff --git a/web_admin/views/article.py b/web_admin/views/article.py index ab55d16..e2cee07 100644 --- a/web_admin/views/article.py +++ b/web_admin/views/article.py @@ -17,7 +17,7 @@ from web_admin.models import Article, ArticleCard, ArticleSurvey, DefaultArticle from web_admin.permission import AdminPermission from web_admin.serializers.article_serializer import (ArticleSerializer, ArticleCardSerializer, DefaultArticleCardImageSerializer, ArticleListSerializer, - ArticleCardlistSerializer) + ArticleCardlistSerializer, ArticleStatusChangeSerializer) USER = get_user_model() @@ -32,7 +32,6 @@ class ArticleViewSet(GenericViewSet, mixins.CreateModelMixin, mixins.UpdateModel queryset = Article filter_backends = (SearchFilter,) search_fields = ['title'] - http_method_names = ['get', 'post', 'put', 'delete'] def get_queryset(self): article = self.queryset.objects.filter(is_deleted=False).prefetch_related( @@ -130,21 +129,22 @@ 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='status-change', url_path='status-change', - detail=True) + @action(methods=['patch'], url_name='status-change', url_path='status-change', + detail=True, serializer_class=ArticleStatusChangeSerializer) def article_status_change(self, request, *args, **kwargs): """ article un-publish or publish api method - :param request: article id + :param request: article id and + { + "is_published": true/false + } :return: success message """ - try: - article = Article.objects.filter(id=kwargs['pk']).first() - article.is_published = False if article.is_published else True - article.save(update_fields=['is_published']) - return custom_response(SUCCESS_CODE["3038"]) - except AttributeError: - return custom_error_response(ERROR_CODE["2041"], response_status=status.HTTP_400_BAD_REQUEST) + article = Article.objects.filter(id=kwargs['pk']).first() + serializer = self.serializer_class(article, data=request.data) + serializer.is_valid(raise_exception=True) + serializer.save() + return custom_response(SUCCESS_CODE["3038"]) @action(methods=['get'], url_name='remove-card', url_path='remove-card', detail=True)