diff --git a/base/messages.py b/base/messages.py index 82d471a..a13c1d6 100644 --- a/base/messages.py +++ b/base/messages.py @@ -65,7 +65,12 @@ ERROR_CODE = { "2036": "Choose valid user", # log in multiple device msg "2037": "You are already log in another device", - "2038": "Choose valid action for task" + "2038": "Choose valid action for task", + "2039": "Add at least one article card or maximum 6", + "2040": "Add at least 5 article survey or maximum 10", + "2041": "Article with given id doesn't exist.", + "2042": "Article Card with given id doesn't exist.", + "2043": "Article Survey with given id doesn't exist." } """Success message code""" SUCCESS_CODE = { @@ -105,6 +110,11 @@ SUCCESS_CODE = { "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.", } """status code error""" STATUS_CODE_ERROR = { diff --git a/web_admin/admin.py b/web_admin/admin.py index 1adf7e6..b1df483 100644 --- a/web_admin/admin.py +++ b/web_admin/admin.py @@ -1,5 +1,10 @@ +""" +web_admin admin file +""" +# django imports from django.contrib import admin +# local imports from web_admin.models import Article, ArticleCard, ArticleSurvey, SurveyOption diff --git a/web_admin/apps.py b/web_admin/apps.py index 9701a1f..2842e02 100644 --- a/web_admin/apps.py +++ b/web_admin/apps.py @@ -1,6 +1,13 @@ +""" +web_admin app file +""" +# django imports from django.apps import AppConfig class WebAdminConfig(AppConfig): + """ + web admin app config + """ default_auto_field = 'django.db.models.BigAutoField' name = 'web_admin' diff --git a/web_admin/models.py b/web_admin/models.py index 0437102..1e49c6b 100644 --- a/web_admin/models.py +++ b/web_admin/models.py @@ -6,6 +6,9 @@ from django.db import models class Article(models.Model): + """ + Article model + """ title = models.CharField(max_length=255) description = models.TextField() created_at = models.DateTimeField(auto_now_add=True) @@ -19,6 +22,9 @@ class Article(models.Model): class ArticleCard(models.Model): + """ + Article Card model + """ article = models.ForeignKey(Article, on_delete=models.CASCADE, related_name='article_cards') title = models.CharField(max_length=255) description = models.TextField() @@ -32,6 +38,9 @@ class ArticleCard(models.Model): class ArticleSurvey(models.Model): + """ + Article Survey model + """ article = models.ForeignKey(Article, on_delete=models.CASCADE, related_name='article_survey') question = models.CharField(max_length=255) points = models.IntegerField() @@ -44,6 +53,9 @@ class ArticleSurvey(models.Model): class SurveyOption(models.Model): + """ + Survey Options model + """ survey = models.ForeignKey(ArticleSurvey, on_delete=models.CASCADE, related_name='survey_options') option = models.CharField(max_length=255) is_answer = models.BooleanField(default=False) diff --git a/web_admin/permission.py b/web_admin/permission.py new file mode 100644 index 0000000..5ecf33a --- /dev/null +++ b/web_admin/permission.py @@ -0,0 +1,26 @@ +""" +web_admin permission classes +""" +# django imports +from rest_framework import permissions + + +class AdminPermission(permissions.BasePermission): + """ + to check for usertype admin only + """ + def has_permission(self, request, view): + """ + Return True if user_type is admin + """ + if request.user.is_superuser: + return True + return False + + def has_object_permission(self, request, view, obj): + """ + check for object level permission + """ + if request.user.is_superuser: + return True + return False diff --git a/web_admin/serializers.py b/web_admin/serializers.py index 23e69e3..57d4278 100644 --- a/web_admin/serializers.py +++ b/web_admin/serializers.py @@ -1,33 +1,55 @@ """ web_admin serializers file """ +# django imports from rest_framework import serializers +from django.conf import settings +# local imports +from base.messages import ERROR_CODE from web_admin.models import Article, ArticleCard, SurveyOption, ArticleSurvey from web_admin.utils import pop_id class ArticleCardSerializer(serializers.ModelSerializer): + """ + Article Card serializer + """ id = serializers.IntegerField(required=False) class Meta: + """ + meta class + """ model = ArticleCard fields = ('id', 'title', 'description') class SurveyOptionSerializer(serializers.ModelSerializer): + """ + survey option serializer + """ id = serializers.IntegerField(required=False) class Meta: + """ + meta class + """ model = SurveyOption fields = ('id', 'option', 'is_answer') class ArticleSurveySerializer(serializers.ModelSerializer): + """ + article survey serializer + """ id = serializers.IntegerField(required=False) survey_options = SurveyOptionSerializer(many=True) class Meta: + """ + meta class + """ model = ArticleSurvey fields = ('id', 'question', 'points', 'survey_options') @@ -46,12 +68,27 @@ class ArticleSerializer(serializers.ModelSerializer): model = Article fields = ('id', 'title', 'description', 'article_cards', 'article_survey') + def validate(self, attrs): + """ + to validate request data + :param attrs: + :return: validated attrs + """ + article_cards = attrs.get('article_cards', None) + article_survey = attrs.get('article_survey', None) + if article_cards is None or len(article_cards) > int(settings.MAX_ARTICLE_CARD): + raise serializers.ValidationError({'details': ERROR_CODE['2039']}) + if article_survey is None or len(article_survey) < int(settings.MIN_ARTICLE_SURVEY) or int( + settings.MAX_ARTICLE_SURVEY) < len(article_survey): + raise serializers.ValidationError({'details': ERROR_CODE['2040']}) + return attrs + def create(self, validated_data): """ to create article. ID in post data dict is for update api. :param validated_data: - :return: success message + :return: article object """ article_cards = validated_data.pop('article_cards') article_survey = validated_data.pop('article_survey') @@ -73,6 +110,12 @@ class ArticleSerializer(serializers.ModelSerializer): return article def update(self, instance, validated_data): + """ + to update article and related table + :param instance: + :param validated_data: + :return: article object + """ article_cards = validated_data.pop('article_cards') article_survey = validated_data.pop('article_survey') instance.title = validated_data.get('title', instance.title) diff --git a/web_admin/tests.py b/web_admin/tests.py index 7ce503c..8800281 100644 --- a/web_admin/tests.py +++ b/web_admin/tests.py @@ -1,3 +1,6 @@ +""" +web_admin test file +""" from django.test import TestCase # Create your tests here. diff --git a/web_admin/urls.py b/web_admin/urls.py index d79fafa..95bc7d9 100644 --- a/web_admin/urls.py +++ b/web_admin/urls.py @@ -5,8 +5,10 @@ web_admin urls file from django.urls import path, include from rest_framework import routers +# local imports from web_admin.views import ArticleViewSet +# initiate router router = routers.SimpleRouter() router.register('article', ArticleViewSet, basename='article') diff --git a/web_admin/views.py b/web_admin/views.py index 88147a1..8003e2c 100644 --- a/web_admin/views.py +++ b/web_admin/views.py @@ -5,50 +5,138 @@ web_admin views file from rest_framework.viewsets import GenericViewSet, mixins from rest_framework.response import Response from rest_framework import status +from rest_framework.decorators import action +from rest_framework.permissions import IsAuthenticated # local imports from account.utils import custom_response, custom_error_response -from web_admin.models import Article +from base.messages import SUCCESS_CODE, ERROR_CODE +from web_admin.models import Article, ArticleCard, ArticleSurvey +from web_admin.permission import AdminPermission from web_admin.serializers import ArticleSerializer class ArticleViewSet(GenericViewSet, mixins.CreateModelMixin, mixins.UpdateModelMixin, mixins.ListModelMixin, mixins.RetrieveModelMixin, mixins.DestroyModelMixin): + """ + article api + """ serializer_class = ArticleSerializer - permission_classes = [] + permission_classes = [IsAuthenticated, AdminPermission] queryset = Article.objects.prefetch_related('article_cards', 'article_survey', - 'article_survey__survey_options') + 'article_survey__survey_options').order_by('-created_at') + + http_method_names = ['get', 'post', 'put', 'delete'] def create(self, request, *args, **kwargs): + """ + article create api method + :param request: + :param args: + :param kwargs: + :return: success message + """ serializer = self.serializer_class(data=request.data) serializer.is_valid(raise_exception=True) serializer.save() - return custom_response("created") + return custom_response(SUCCESS_CODE["3027"]) def update(self, request, *args, **kwargs): + """ + article update api method + :param request: + :param args: + :param kwargs: + :return: success message + """ article = self.queryset.filter(id=kwargs['pk']).first() serializer = self.serializer_class(article, data=request.data) serializer.is_valid(raise_exception=True) serializer.save() - return custom_response("updated") + return custom_response(SUCCESS_CODE["3028"]) def list(self, request, *args, **kwargs): - # queryset = Article.objects.prefetch_related('article_cards', - # 'article_survey', - # 'article_survey__survey_options') + """ + article list api method + :param request: + :param args: + :param kwargs: + :return: list of article + """ + queryset = self.queryset.filter(is_deleted=False) paginator = self.pagination_class() - paginated_queryset = paginator.paginate_queryset(self.queryset, request) + paginated_queryset = paginator.paginate_queryset(queryset, request) serializer = self.serializer_class(paginated_queryset, many=True) return custom_response(None, data=serializer.data) def retrieve(self, request, *args, **kwargs): - queryset = self.queryset.filter(id=kwargs['pk']) + """ + article detail api method + :param request: + :param args: + :param kwargs: + :return: article detail data + """ + queryset = self.queryset.filter(id=kwargs['pk'], is_deleted=False) serializer = self.serializer_class(queryset, many=True) return custom_response(None, data=serializer.data) def destroy(self, request, *args, **kwargs): + """ + article delete (soft delete) api method + :param request: + :param args: + :param kwargs: + :return: success message + """ article = self.queryset.filter(id=kwargs['pk']).update(is_deleted=True) if article: - return custom_response("deleted") - return custom_error_response("article doesn't exist", status.HTTP_400_BAD_REQUEST) + return custom_response(SUCCESS_CODE["3029"]) + return custom_error_response(ERROR_CODE["2041"], status.HTTP_400_BAD_REQUEST) + + @action(methods=['get'], url_name='', url_path='', detail=False) + def search_article(self, request): + """ + article search api method + :param request: + :return: searched article + """ + search = request.GET.get('search') + queryset = self.queryset.filter(title__icontains=search) + 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) + + @action(methods=['get'], url_name='remove_card', url_path='remove_card', + detail=True, serializer_class=None) + def remove_article_card(self, request, *args, **kwargs): + """ + article card remove (delete) api method + :param request: + :param args: + :param kwargs: + :return: success message + """ + try: + ArticleCard.objects.filter(id=kwargs['pk']).first().delete() + return custom_response(SUCCESS_CODE["3030"]) + 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', + detail=True, serializer_class=None) + def remove_article_survey(self, request, *args, **kwargs): + """ + article survey remove (delete) api method + :param request: + :param args: + :param kwargs: + :return: success message + """ + try: + ArticleSurvey.objects.filter(id=kwargs['pk']).first().delete() + return custom_response(SUCCESS_CODE["3031"]) + except AttributeError: + return custom_error_response(ERROR_CODE["2043"], response_status=status.HTTP_400_BAD_REQUEST) diff --git a/zod_bank/settings.py b/zod_bank/settings.py index a319bbe..b107ffd 100644 --- a/zod_bank/settings.py +++ b/zod_bank/settings.py @@ -233,6 +233,10 @@ ALIYUN_OSS_BUCKET_NAME = os.getenv('ALIYUN_OSS_BUCKET_NAME') ALIYUN_OSS_ENDPOINT = os.getenv('ALIYUN_OSS_ENDPOINT') ALIYUN_OSS_REGION = os.getenv('ALIYUN_OSS_REGION') +MAX_ARTICLE_CARD = os.getenv('MAX_ARTICLE_CARD', 6) +MIN_ARTICLE_SURVEY = os.getenv('MIN_ARTICLE_SURVEY', 5) +MAX_ARTICLE_SURVEY = os.getenv('MAX_ARTICLE_SURVEY', 10) + # define static url STATIC_URL = 'static/' # define static root