From 89deae92b35643153f6cf22ea58b43337bf73314 Mon Sep 17 00:00:00 2001 From: akhdanre Date: Thu, 1 May 2025 19:59:09 +0700 Subject: [PATCH] feat: search quiz done --- app/blueprints/quiz.py | 9 +++++ app/controllers/quiz_controller.py | 7 ++++ app/models/entities/quiz_entity.py | 2 +- app/repositories/quiz_repositroy.py | 62 +++++++++++++++++++++++++++++ app/services/quiz_service.py | 31 +++++++++++++++ 5 files changed, 110 insertions(+), 1 deletion(-) diff --git a/app/blueprints/quiz.py b/app/blueprints/quiz.py index ba09b4b..44f953f 100644 --- a/app/blueprints/quiz.py +++ b/app/blueprints/quiz.py @@ -59,3 +59,12 @@ def get_user_quiz( page = request.args.get("page", default=1, type=int) page_size = request.args.get("page_size", default=10, type=int) return controller.get_user_quiz(user_id=user_id, page=page, page_size=page_size) + + +@quiz_bp.route("/search", methods=["GET"]) +@inject +def search_quiz(controller: QuizController = Provide[Container.quiz_controller]): + keyword = request.args.get("keyword", "") + page = int(request.args.get("page", 1)) + limit = int(request.args.get("limit", 10)) + return controller.search_quiz(keyword=keyword, page=page, limit=limit) diff --git a/app/controllers/quiz_controller.py b/app/controllers/quiz_controller.py index af4f2b2..a64fc75 100644 --- a/app/controllers/quiz_controller.py +++ b/app/controllers/quiz_controller.py @@ -127,3 +127,10 @@ class QuizController: ) except Exception as e: return make_error_response(e) + + def search_quiz(self, keyword: str, page: int, limit: int): + try: + result = self.quiz_service.search_quiz(keyword, page, limit) + return make_response(message="success", data=result) + except Exception as e: + return make_error_response(e) diff --git a/app/models/entities/quiz_entity.py b/app/models/entities/quiz_entity.py index 93a3ac3..f3726d1 100644 --- a/app/models/entities/quiz_entity.py +++ b/app/models/entities/quiz_entity.py @@ -10,7 +10,7 @@ class QuizEntity(BaseModel): author_id: Optional[str] = None title: str description: Optional[str] = None - subject: str + # subject: str is_public: bool = False date: Optional[datetime] = None total_quiz: Optional[int] = 0 diff --git a/app/repositories/quiz_repositroy.py b/app/repositories/quiz_repositroy.py index cda6403..478b223 100644 --- a/app/repositories/quiz_repositroy.py +++ b/app/repositories/quiz_repositroy.py @@ -21,6 +21,68 @@ class QuizRepository: return QuizEntity(**data) return None + # def search_by_title_or_category( + # self, keyword: str, page: int, page_size: int + # ) -> List[QuizEntity]: + # skip = (page - 1) * page_size + # pipeline = [ + # { + # "$lookup": { + # "from": "category", + # "localField": "category_id", + # "foreignField": "_id", + # "as": "category_info", + # } + # }, + # {"$unwind": "$category_info"}, + # { + # "$match": { + # "$or": [ + # {"title": {"$regex": keyword, "$options": "i"}}, + # {"category_info.name": {"$regex": keyword, "$options": "i"}}, + # ] + # } + # }, + # {"$skip": skip}, + # {"$limit": page_size}, + # ] + # cursor = self.collection.aggregate(pipeline) + # return [QuizEntity(**doc) for doc in cursor] + + def search_by_title_or_category( + self, keyword: str, page: int, page_size: int + ) -> List[QuizEntity]: + skip = (page - 1) * page_size + cursor = ( + self.collection.find( + { + "$and": [ + {"is_public": True}, + { + "$or": [ + {"title": {"$regex": keyword, "$options": "i"}}, + # {"category": {"$regex": keyword, "$options": "i"}}, + ] + }, + ] + } + ) + .skip(skip) + .limit(page_size) + ) + + return [QuizEntity(**doc) for doc in cursor] + + def count_by_search(self, keyword: str) -> int: + return self.collection.count_documents( + { + "$or": [ + {"title": {"$regex": keyword, "$options": "i"}}, + {"category": {"$regex": keyword, "$options": "i"}}, + ] + } + ) + def get_by_ids(self, quiz_ids: List[str]) -> Optional[List[QuizEntity]]: object_ids = [ObjectId(qid) for qid in quiz_ids] cursor = self.collection.find({"_id": {"$in": object_ids}}) diff --git a/app/services/quiz_service.py b/app/services/quiz_service.py index 16d8063..e0a7a27 100644 --- a/app/services/quiz_service.py +++ b/app/services/quiz_service.py @@ -19,6 +19,37 @@ class QuizService: raise DataNotFoundException("Quiz not found") return map_quiz_entity_to_schema(data) + def search_quiz( + self, keyword: str, page: int = 1, page_size: int = 10 + ) -> tuple[list[RecomendationResponse], int]: + if not keyword: + raise ValidationException("Keyword cannot be empty.") + + quizzes = self.quiz_repository.search_by_title_or_category( + keyword, page, page_size + ) + total = self.quiz_repository.count_by_search(keyword) + print("quiz len", len(quizzes)) + mapped_quizzes = [] + for quiz in quizzes: + author = self.user_repostory.get_user_by_id(user_id=quiz.author_id) + if author is None: + print(quiz.author_id, "skipped") + continue # or handle default name + + mapped_quizzes.append( + RecomendationResponse( + quiz_id=str(quiz.id), + author_id=str(author.id), + author_name=author.name, + title=quiz.title, + description=quiz.description, + date=quiz.date.strftime("%d-%B-%Y"), + ) + ) + + return mapped_quizzes, total + def get_user_quiz( self, user_id: str, page: int = 1, page_size: int = 10 ) -> UserQuizListResponse: