diff --git a/config/basemodel.py b/config/basemodel.py index 8c7b0dd..b22417d 100644 --- a/config/basemodel.py +++ b/config/basemodel.py @@ -1,10 +1,23 @@ from django.db import models +from django.http import JsonResponse +from rest_framework import status class BaseModel(models.Model): created_at = models.DateTimeField(auto_now_add=True) updated_at = models.DateTimeField(auto_now=True) + # deleted_at = models.DateTimeField(blank=True, null=True) class Meta: abstract = True + + +class ApiResponse: + @staticmethod + def on_success(result, response_status=status.HTTP_200_OK): + return JsonResponse({'isSuccess': True, 'result': result}, status=response_status) + + @staticmethod + def on_fail(message, response_status=status.HTTP_400_BAD_REQUEST): + return JsonResponse({'isSuccess': False, 'message': message}, status=response_status) diff --git a/diary/migrations/0004_keywords_questions_delete_quizs.py b/diary/migrations/0004_keywords_questions_delete_quizs.py new file mode 100644 index 0000000..122c6b4 --- /dev/null +++ b/diary/migrations/0004_keywords_questions_delete_quizs.py @@ -0,0 +1,43 @@ +# Generated by Django 5.0.2 on 2024-03-06 08:50 + +import django.db.models.deletion +from django.db import migrations, models + + +class Migration(migrations.Migration): + + dependencies = [ + ('diary', '0003_remove_diary_content_alter_sentences_diary_quizs_and_more'), + ] + + operations = [ + migrations.CreateModel( + name='Keywords', + fields=[ + ('id', models.BigAutoField(auto_created=True, primary_key=True, serialize=False, verbose_name='ID')), + ('created_at', models.DateTimeField(auto_now_add=True)), + ('updated_at', models.DateTimeField(auto_now=True)), + ('keyword', models.CharField(max_length=100)), + ('sentence', models.ForeignKey(on_delete=django.db.models.deletion.CASCADE, related_name='keywords', to='diary.sentences')), + ], + options={ + 'abstract': False, + }, + ), + migrations.CreateModel( + name='Questions', + fields=[ + ('id', models.BigAutoField(auto_created=True, primary_key=True, serialize=False, verbose_name='ID')), + ('created_at', models.DateTimeField(auto_now_add=True)), + ('updated_at', models.DateTimeField(auto_now=True)), + ('question', models.TextField()), + ('keyword', models.ForeignKey(on_delete=django.db.models.deletion.CASCADE, related_name='questions', to='diary.keywords')), + ], + options={ + 'abstract': False, + }, + ), + migrations.DeleteModel( + name='Quizs', + ), + ] diff --git a/diary/models.py b/diary/models.py index fae0902..0b7af6a 100644 --- a/diary/models.py +++ b/diary/models.py @@ -20,8 +20,12 @@ class Sentences(BaseModel): diary = models.ForeignKey('diary.Diary', related_name='sentences', on_delete=models.CASCADE) -class Quizs(BaseModel): +class Keywords(BaseModel): + keyword = models.CharField(max_length=100) + + sentence = models.ForeignKey('diary.Sentences', related_name='keywords', on_delete=models.CASCADE) + +class Questions(BaseModel): question = models.TextField() - answer = models.CharField(max_length=50) - sentence = models.ForeignKey('diary.Sentences', related_name='quizs', on_delete=models.CASCADE) \ No newline at end of file + keyword = models.ForeignKey('diary.Keywords', related_name='questions', on_delete=models.CASCADE) \ No newline at end of file diff --git a/diary/serializers.py b/diary/serializers.py index a72db12..8155fb9 100644 --- a/diary/serializers.py +++ b/diary/serializers.py @@ -1,8 +1,8 @@ -from rest_framework import serializers +from rest_framework import serializers, status from users.models import User from users.serializers import UserSafeSerializer -from .models import Diary, Sentences, Quizs +from .models import Diary, Sentences, Keywords, Questions class DiarySerializer(serializers.ModelSerializer): @@ -12,6 +12,7 @@ class Meta: model = Diary fields = '__all__' + class DiarySimpleSerializer(serializers.ModelSerializer): user = UserSafeSerializer(read_only=True) @@ -19,6 +20,7 @@ class Meta: model = Diary fields = ['id', 'user', 'title'] + class SentenceSerializer(serializers.ModelSerializer): diay = DiarySerializer(read_only=True) @@ -26,6 +28,7 @@ class Meta: model = Sentences fields = '__all__' + class SentenceSimpleSerializer(serializers.ModelSerializer): diary = DiarySimpleSerializer(read_only=True) @@ -33,13 +36,23 @@ class Meta: model = Sentences fields = ['id', 'diary', 'sentence'] -class QuizSerializer(serializers.ModelSerializer): - sentences = SentenceSerializer(read_only=True) + +class KeywordSerializer(serializers.ModelSerializer): + sentence = SentenceSerializer(read_only=True) + + class Meta: + model = Keywords + fields = '__all__' + + +class QuestionSerializer(serializers.ModelSerializer): + keyword = KeywordSerializer(read_only=True) class Meta: - model = Quizs + model = Questions fields = '__all__' + class DiaryCreateRequest(serializers.ModelSerializer): userId = serializers.IntegerField() @@ -53,28 +66,28 @@ def to_diary(self, user: User) -> DiarySerializer: newDiary.save(user=user) return newDiary + class WriteRequest(serializers.Serializer): userId = serializers.IntegerField() title = serializers.CharField(max_length=100) content = serializers.CharField() - class Meta: - model = Diary - fields = '__all__' + def is_valid(self, raise_exception=False): + super_valid = super().is_valid() + # 유효하지 않다면 False, 400 반환 + if not super_valid: + return False, status.HTTP_400_BAD_REQUEST - def create(self, validated_data): - user_id = validated_data.pop('userId') - try: - user = User.objects.get(pk=user_id) - except User.DoesNotExist: - raise serializers.ValidationError("유저가 존재하지 않습니다.") - - validated_data['user'] = user - content_data = validated_data.pop('content') - diary = Diary.objects.create(**validated_data) - Sentences.objects.create(diary=diary, sentence=content_data) + # 유효하다면 userId가 존재하는지 확인 + is_user_exist = User.objects.filter(id=self.data['userId']).exists() + + # 존재하지 않는다면 False, 404 반환 + if not is_user_exist: + self._errors['userId'] = [f'userId: {self.data.get("userId")} 가 존재하지 않습니다.'] + return False, status.HTTP_404_NOT_FOUND + + return True, status.HTTP_200_OK - return diary class UpdateRequest(serializers.Serializer): diaryId = serializers.IntegerField() @@ -82,27 +95,62 @@ class UpdateRequest(serializers.Serializer): title = serializers.CharField(max_length=100) content = serializers.CharField() - class Meta: - model = Diary - fields = '__all__' + def is_valid(self, raise_exception=False): + super_valid = super().is_valid() + # 유효하지 않다면 False, 400 반환 + if not super_valid: + return False, status.HTTP_400_BAD_REQUEST + + # userId가 존재하는지 확인 + is_user_exist = User.objects.filter(id=self.data['userId']).exists() + # 존재하지 않는다면 False, 404 반환 + if not is_user_exist: + self._errors['userId'] = [f'userId: {self.data.get("userId")} 가 존재하지 않습니다.'] + return False, status.HTTP_404_NOT_FOUND - def create(self, validated_data): - diary_id = validated_data.pop('diaryId') - user_id = validated_data.pop('userId') - try: - user = User.objects.get(pk=user_id) - except User.DoesNotExist: - raise serializers.ValidationError("유저가 존재하지 않습니다.") + # diaryId가 존재하는지 확인 + is_diary_exist = Diary.objects.filter(id=self.data['diaryId']).exists() + # 존재하지 않는다면 False, 404 반환 + if not is_diary_exist: + self._errors['diaryId'] = [f'diaryId: {self.data.get("diaryId")} 가 존재하지 않습니다.'] + return False, status.HTTP_404_NOT_FOUND - validated_data['user'] = user - content_data = validated_data.pop('content') - new_diary = Diary.objects.create(**validated_data) - Sentences.objects.create(diary=new_diary, sentence=content_data) + return True, status.HTTP_200_OK - return new_diary class GetUserRequest(serializers.Serializer): userId = serializers.IntegerField() + def is_valid(self, raise_exception=False): + super_valid = super().is_valid() + # 유효하지 않다면 False, 400 반환 + if not super_valid: + return False, status.HTTP_400_BAD_REQUEST + + # userId가 존재하는지 확인 + is_user_exist = User.objects.filter(id=self.data['userId']).exists() + # 존재하지 않는다면 False, 404 반환 + if not is_user_exist: + self._errors['userId'] = [f'userId: {self.data.get("userId")} 가 존재하지 않습니다.'] + return False, status.HTTP_404_NOT_FOUND + + return True, status.HTTP_200_OK + + class GetDiaryRequest(serializers.Serializer): - diaryId = serializers.IntegerField() \ No newline at end of file + diaryId = serializers.IntegerField() + + def is_valid(self, raise_exception=False): + super_valid = super().is_valid() + # 유효하지 않다면 False, 400 반환 + if not super_valid: + return False, status.HTTP_400_BAD_REQUEST + + # diaryId가 존재하는지 확인 + is_diary_exist = Diary.objects.filter(id=self.data['diaryId']).exists() + # 존재하지 않는다면 False, 404 반환 + if not is_diary_exist: + self._errors['diaryId'] = [f'diaryId: {self.data.get("diaryId")} 가 존재하지 않습니다.'] + return False, status.HTTP_404_NOT_FOUND + + return True, status.HTTP_200_OK diff --git a/diary/textrank.py b/diary/textrank.py index 9876e85..c812434 100644 --- a/diary/textrank.py +++ b/diary/textrank.py @@ -86,8 +86,8 @@ def __init__(self, sentence): # 단어 가중치 그래프, 단어 사전 을 저장 && 단어 사전 = {index: 단어} self.words_graph_vocab = np.dot(cnt_vec_mat.T, cnt_vec_mat), {vocab[word]: word for word in vocab} - def get_sent_graph_vocab(self): - return self.sent_graph_vocab + # def get_sent_graph_vocab(self): + # return self.sent_graph_vocab def get_words_graph_vocab(self): return self.words_graph_vocab @@ -118,26 +118,24 @@ def __init__(self, content): # 명사 추출 nouns = SentenceTokenizer.get_nouns(self.sentences) - # 가중치 그래프 객체 생성 - matrix = GraphMatrix(nouns) - # 문장별 가중치 그래프 - sent_graph, snet_vocab = matrix.get_sent_graph_vocab() - # 단어별 가중치 그래프 - words_graph, word_vocab = matrix.get_words_graph_vocab() - - # (문장, index, 가중치) 리스트 생성 - sent_rank = [(snet_vocab[index], index, weight) for index, weight in Rank.get_ranks(sent_graph).items()] - # weight 기준으로 정렬 - self.sorted_sent_rank = sorted(sent_rank, key=lambda k: k[2], reverse=True) - - # (단어, index, 가중치) 리스트 생성 - word_rank_idx = [(word_vocab[index], index, weight) for index, weight in Rank.get_ranks(words_graph).items()] - # weight 기준으로 정렬 - self.sorted_word_rank = sorted(word_rank_idx, key=lambda k: k[2], reverse=True) + if nouns: + # 가중치 그래프 객체 생성 + matrix = GraphMatrix(nouns) + # 문장별 가중치 그래프 [문장수, 문장수], {index: 문장} 사전 + # sent_graph, sent_vocab = matrix.get_sent_graph_vocab() + # 단어별 가중치 그래프 [단어수, 단어수], {index: 단어} 사전 + words_graph, word_vocab = matrix.get_words_graph_vocab() + + # (단어, index, 가중치) 리스트 생성 + word_rank_idx = [(word_vocab[index], index, weight) for index, weight in Rank.get_ranks(words_graph).items()] + # weight 기준으로 정렬 + self.sorted_word_rank = sorted(word_rank_idx, key=lambda k: k[2], reverse=True) + else: + self.sorted_word_rank = [] # sent_size 개의 문장 요약 - def summarize(self, sent_size=3): - return [sentence for sentence, index, weight in self.sorted_sent_rank[:sent_size]] + # def summarize(self, sent_size=3): + # return [sentence for sentence, index, weight in self.sorted_sent_rank[:sent_size]] def get_keywords(self, keyword_size=3): # 단어 가중치 상위 word_size개 word만 추출 diff --git a/diary/urls.py b/diary/urls.py index eb8ea25..ed90009 100644 --- a/diary/urls.py +++ b/diary/urls.py @@ -1,11 +1,9 @@ -from django.urls import path, include -from rest_framework.routers import DefaultRouter -from .views import DiaryView, WriteView, GetDiarybyUserView, GetQuizView, UpdateView +from django.urls import path +from .views import WriteView, GetDiaryByUserView, GetQuizView, UpdateView urlpatterns = [ - path('', DiaryView.as_view()), path('write', WriteView.as_view()), - path('list', GetDiarybyUserView.as_view()), + path('list', GetDiaryByUserView.as_view()), path('quiz', GetQuizView.as_view()), path('update', UpdateView.as_view()) ] diff --git a/diary/views.py b/diary/views.py index a4d5563..0f97417 100644 --- a/diary/views.py +++ b/diary/views.py @@ -1,135 +1,171 @@ -from django.core.handlers.wsgi import WSGIRequest -from django.http import HttpResponse, JsonResponse -from django.views.decorators.csrf import csrf_exempt -from rest_framework.parsers import JSONParser +from drf_yasg.utils import swagger_auto_schema from rest_framework.views import APIView -from rest_framework.response import Response -from rest_framework import status -from diary.models import Diary, Sentences, Quizs +from diary.models import Diary, Sentences, Keywords, Questions +from config.basemodel import ApiResponse from diary.serializers import * from users.models import User - -from drf_yasg.utils import swagger_auto_schema - from .textrank import TextRank, make_quiz -class DiaryView(APIView): - def get(self, request: WSGIRequest) -> HttpResponse: - findDiaries = Diary.objects.all() - serializer = DiarySerializer(findDiaries, many=True) - return JsonResponse(serializer.data, safe=False) - - @csrf_exempt - def post(self, request: WSGIRequest) -> HttpResponse: - data = JSONParser().parse(request) - request = DiaryCreateRequest(data=data) - is_valid = request.is_valid() - print(request.data) - if is_valid: - findUser = User.objects.get(pk=request.data["userId"]) - newDiary = request.to_diary(findUser) - - return JsonResponse(newDiary.data, status=201) - else: - return JsonResponse(DiarySerializer(data=data).errors, status=400) - class WriteView(APIView): - @swagger_auto_schema(operation_description="일기 작성", request_body=WriteRequest, responses={"201":'작성 성공'}) + @swagger_auto_schema(operation_description="일기 작성", request_body=WriteRequest, responses={"201": '작성 성공'}) def post(self, request): - serializer = WriteRequest(data=request.data) + requestSerial = WriteRequest(data=request.data) + + isValid, response_status = requestSerial.is_valid() + # 유효성 검사 통과하지 못한 경우 + if not isValid: + return ApiResponse.on_fail(requestSerial.errors, response_status=response_status) - if serializer.is_valid(): - user_id = serializer.validated_data.get('userId') - try: - user = User.objects.get(id=user_id) - except User.DoesNotExist: - return JsonResponse({'isSuccess': False, 'message': '사용자를 찾을 수 없습니다.'}, status=status.HTTP_400_BAD_REQUEST) + request = requestSerial.validated_data - diary = serializer.save() + # user 가져오기 + user_id = request.get('userId') + findUser = User.objects.get(id=user_id) - content = Sentences.objects.create(sentence=serializer.validated_data.get('content'), diary=diary) - memory = TextRank(content.sentence) - question, answer = make_quiz(memory, keyword_size=5) + # Diary 객체 생성 + newDiary = Diary.objects.create(user=findUser, title=request.get('title')) - for q, a in zip(question, answer): - Quizs.objects.create(question=q, answer=a, sentence=content) + content = request.get('content') + # Sentences 객체 생성 + newSentence = Sentences.objects.create(sentence=content, diary=newDiary) - return JsonResponse({'isSuccess': True, 'result': SentenceSimpleSerializer(content).data}, status=status.HTTP_201_CREATED) + # 키워드 추출 + memory = TextRank(content=content) - return JsonResponse(serializer.errors, status=status.HTTP_400_BAD_REQUEST) + if memory is None: + return ApiResponse.on_success( + result=SentenceSimpleSerializer(newSentence).data, + response_status=status.HTTP_201_CREATED + ) + + # 키워드 추출 후 가중치가 높은 키워드 5개로 퀴즈 생성 + question, keyword = make_quiz(memory, keyword_size=5) + + # 각 키워드별로 Question 생성 + for q, k in zip(question, keyword): + newKeyword = Keywords.objects.create(keyword=k, sentence=newSentence) + Questions.objects.create(question=q, keyword=newKeyword) + + return ApiResponse.on_success( + result=SentenceSimpleSerializer(newSentence).data, + response_status=status.HTTP_201_CREATED + ) class UpdateView(APIView): - @swagger_auto_schema(operation_description="일기 수정", request_body=UpdateRequest, responses={"201":'작성 성공'}) + @swagger_auto_schema(operation_description="일기 수정", request_body=UpdateRequest, responses={"201": '작성 성공'}) def post(self, request): - serializer = UpdateRequest(data=request.data) + requestSerial = UpdateRequest(data=request.data) + + isValid, response_status = requestSerial.is_valid() - if serializer.is_valid(): - user_id = serializer.validated_data.get('userId') - diary_id = serializer.validated_data.get('diaryId') - try: - user = User.objects.get(id=user_id) - except User.DoesNotExist: - return JsonResponse({'isSuccess': False, 'message': '사용자를 찾을 수 없습니다.'}, status=status.HTTP_400_BAD_REQUEST) + # 유효성 검사 통과하지 못한 경우 + if not isValid: + return ApiResponse.on_fail(requestSerial.errors, response_status=response_status) - try: - deleteDiary = Diary.objects.get(id=diary_id) - Diary.delete(deleteDiary) + # 유효성 검사 통과한 경우 + request = requestSerial.validated_data - updateDiary = serializer.save() + # Diary 가져오기 + diary_id = request.get('diaryId') + findDiary = Diary.objects.get(id=diary_id) - content = Sentences.objects.create(sentence=serializer.validated_data.get('content'), diary=updateDiary) - memory = TextRank(content.sentence) - question, answer = make_quiz(memory, keyword_size=5) + # Diary와 연관된 모든 Sentence, Question, Keyword 삭제 + sentences = findDiary.sentences.all() + for sentence in sentences: + sentence.keywords.all().delete() + sentence.delete() + + content = request.get('content') + newSentence = Sentences.objects.create(sentence=content, diary=findDiary) + + # 키워드 추출 + memory = TextRank(content=content) + + if memory is None: + return ApiResponse.on_success( + result=SentenceSimpleSerializer(sentence).data, + response_status=status.HTTP_201_CREATED + ) + + # 키워드 추출 후 가중치가 높은 키워드 5개로 퀴즈 생성 + question, keyword = make_quiz(memory, keyword_size=5) - for q, a in zip(question, answer): - Quizs.objects.create(question=q, answer=a, sentence=content) + # 각 키워드별로 Question 생성 + for q, k in zip(question, keyword): + newKeyword = Keywords.objects.create(keyword=k, sentence=newSentence) + Questions.objects.create(question=q, keyword=newKeyword) - return JsonResponse({'isSuccess': True, 'result': SentenceSimpleSerializer(content).data}, status=status.HTTP_201_CREATED) - except Diary.DoesNotExist: - return JsonResponse({'isSuccess': False, 'message': '일기를 찾을 수 없습니다.'}, status=status.HTTP_400_BAD_REQUEST) - return JsonResponse(serializer.errors, status=status.HTTP_400_BAD_REQUEST) + return ApiResponse.on_success( + result=SentenceSimpleSerializer(newSentence).data, + response_status=status.HTTP_201_CREATED + ) -class GetDiarybyUserView(APIView): - @swagger_auto_schema(operation_description="유저의 일기 조회", query_serializer=GetUserRequest, responses={"200":DiarySerializer}) +class GetDiaryByUserView(APIView): + @swagger_auto_schema(operation_description="유저의 일기 조회", query_serializer=GetUserRequest, + response={"200": DiarySerializer}) def get(self, request): - serializer = GetUserRequest(data=request.query_params) - - if serializer.is_valid(): - try: - user = User.objects.get(id=serializer.validated_data.get('userId')) - diaries = Diary.objects.filter(user=user) - serializer = DiarySerializer(diaries, many=True) - return JsonResponse(serializer.data, safe=False, status=status.HTTP_200_OK) - except User.DoesNotExist: - return JsonResponse({'isSuccess' : False, 'message' : '사용자를 찾을 수 없습니다.'}, status=status.HTTP_201_CREATED) - - return Response(serializer.errors, status=status.HTTP_400_BAD_REQUEST) - - + requestSerial = GetUserRequest(data=request.query_params) + + isValid, response_status = requestSerial.is_valid() + + # 유효성 검사 통과하지 못한 경우 + if not isValid: + return ApiResponse.on_fail(requestSerial.errors, response_status=response_status) + + # 유효성 검사 통과한 경우 + request = requestSerial.validated_data + + # User 가져오기 + user_id = request.get('userId') + findUser = User.objects.get(id=user_id) + + # User와 연관된 모든 Diary 가져오기 + findDiaries = Diary.objects.filter(user=findUser) + + return ApiResponse.on_success( + result=DiarySerializer(findDiaries, many=True).data, + response_status=status.HTTP_200_OK + ) + + class GetQuizView(APIView): - @swagger_auto_schema(operation_description="일기회상 퀴즈", query_serializer=GetDiaryRequest, responses={"200":"퀴즈"}) + @swagger_auto_schema(operation_description="일기회상 퀴즈", query_serializer=GetDiaryRequest, + responses={"200": "퀴즈"}) def get(self, request): - serializer = GetDiaryRequest(data=request.query_params) + requestSerial = GetDiaryRequest(data=request.query_params) - if serializer.is_valid(): - diary_id = serializer.validated_data.get('diaryId') - try: - diary = Diary.objects.get(id=diary_id) - except Diary.DoesNotExist: - return JsonResponse({'isSuccess': False, 'message': '해당 일기를 찾을 수 없습니다.'}, status=status.HTTP_404_NOT_FOUND) + isValid, response_status = requestSerial.is_valid() + if not isValid: + return ApiResponse.on_fail(requestSerial.errors, response_status=response_status) - sentences = diary.sentences.all() - quizs = [] + # 유효성 검사 통과한 경우 + request = requestSerial.validated_data - for sentence in sentences: - quizs.extend(sentence.quizs.all()) - - serializer = QuizSerializer(quizs, many=True) + # Diary 가져오기 + diary_id = request.get('diaryId') + findDiary = Diary.objects.get(id=diary_id) - return JsonResponse(serializer.data, safe=False, status=status.HTTP_200_OK) - - return JsonResponse(serializer.errors, status=status.HTTP_400_BAD_REQUEST) \ No newline at end of file + # Diary와 연관된 모든 Sentence 가져오기 + sentences = findDiary.sentences.all() + + # 모든 Sentence 와 연관된 Question 가져오기 + question_keyword = [] + for sentence in sentences: + for keyword in sentence.keywords.all(): + question_keyword.append({ + "Q": keyword.questions.first().question, + "A": keyword.keyword + }) + + return ApiResponse.on_success( + result=question_keyword, + response_status=status.HTTP_200_OK + ) + + +""" +""" \ No newline at end of file