feat: working on quiz

This commit is contained in:
akhdanre 2025-04-27 00:30:47 +07:00
parent 019eb2ecc9
commit 7a521b9ca2
16 changed files with 213 additions and 23 deletions

View File

@ -4,22 +4,46 @@ from dependency_injector.wiring import inject, Provide
from controllers import QuizController from controllers import QuizController
quiz_bp = Blueprint( quiz_bp = Blueprint("quiz", __name__, url_prefix="/quiz")
"quiz",
__name__,
)
@quiz_bp.route("/quiz", methods=["POST"]) @quiz_bp.route("/", methods=["POST"])
@inject @inject
def create_quiz(controller: QuizController = Provide[Container.quiz_controller]): def create_quiz(controller: QuizController = Provide[Container.quiz_controller]):
reqBody = request.get_json() reqBody = request.get_json()
return controller.create_quiz(reqBody) return controller.create_quiz(reqBody)
@quiz_bp.route("/quiz/<quiz_id>", methods=["GET"]) @quiz_bp.route("/<quiz_id>", methods=["GET"])
@inject @inject
def get_quiz( def get_quiz(
quiz_id: str, controller: QuizController = Provide[Container.quiz_controller] quiz_id: str, controller: QuizController = Provide[Container.quiz_controller]
): ):
return controller.get_quiz(quiz_id) return controller.get_quiz(quiz_id)
@quiz_bp.route("/answer", methods=["POST"])
@inject
def submit_answer(controller: QuizController = Provide[Container.quiz_controller]):
req_body = request.get_json()
return controller.submit_answer(req_body)
@quiz_bp.route("/answer", methods=["GET"])
@inject
def get_answer(controller: QuizController = Provide[Container.quiz_controller]):
quiz_id = request.args.get("quiz_id")
user_id = request.args.get("user_id")
session_id = request.args.get("session_id")
return controller.get_answer(
quiz_id=quiz_id, user_id=user_id, session_id=session_id
)
@quiz_bp.route("/recomendation", methods=["GET"])
@inject
def get_quiz_recommendation(
controller: QuizController = Provide[Container.quiz_controller],
):
return controller.get_quiz_recommendation()

View File

@ -1,14 +1,15 @@
from flask import jsonify from flask import jsonify
from pydantic import ValidationError from pydantic import ValidationError
from schemas.requests import QuizCreateSchema from schemas.requests import QuizCreateSchema, UserAnswerSchema
from schemas.response import QuizCreationResponse from schemas.response import QuizCreationResponse
from services import QuizService from services import QuizService, AnswerService
from helpers import make_response, make_error_response from helpers import make_response, make_error_response
class QuizController: class QuizController:
def __init__(self, quiz_service: QuizService): def __init__(self, quiz_service: QuizService, answer_service: AnswerService):
self.quiz_service = quiz_service self.quiz_service = quiz_service
self.answer_service = answer_service
def get_quiz(self, quiz_id): def get_quiz(self, quiz_id):
try: try:
@ -50,3 +51,34 @@ class QuizController:
if not success: if not success:
return jsonify({"error": "Quiz not found"}), 400 return jsonify({"error": "Quiz not found"}), 400
return jsonify({"message": "Quiz deleted"}), 200 return jsonify({"message": "Quiz deleted"}), 200
def quiz_recomendation(self):
try:
result = self.quiz_service.get_quiz_recommendation()
if not result:
return make_response(message="Quiz not found", status_code=404)
return make_response(message="Quiz Found", data=result.dict())
except Exception as e:
return make_error_response(e)
def submit_answer(self, answer_data):
try:
# Assuming answer_data is a dictionary with the necessary fields
answer_obj = UserAnswerSchema(**answer_data)
answer_id = self.answer_service.create_answer(answer_obj)
return make_response(
message="Answer submitted",
data={"answer_id": answer_id},
status_code=201,
)
except ValidationError as e:
return make_response(e.errors(), status_code=400)
except Exception as e:
return make_error_response(e)
def get_answer(self, quiz_id, user_id, session_id):
try:
# self.answer_service.
print("yps")
except Exception as e:
return make_error_response(e)

View File

@ -1,12 +1,7 @@
from dependency_injector import containers, providers from dependency_injector import containers, providers
from controllers import UserController from controllers import UserController, AuthController, QuizController
from repositories.user_repository import UserRepository from repositories import UserRepository, QuizRepository, UserAnswerRepository
from services import UserService, AuthService from services import UserService, AuthService, QuizService, AnswerService
from controllers import AuthController
from flask_pymongo import PyMongo
from repositories import QuizRepository
from services import QuizService
from controllers import QuizController
class Container(containers.DeclarativeContainer): class Container(containers.DeclarativeContainer):
@ -17,13 +12,15 @@ class Container(containers.DeclarativeContainer):
# repository # repository
user_repository = providers.Factory(UserRepository, mongo.provided.db) user_repository = providers.Factory(UserRepository, mongo.provided.db)
quiz_repository = providers.Factory(QuizRepository, mongo.provided.db) quiz_repository = providers.Factory(QuizRepository, mongo.provided.db)
answer_repository = providers.Factory(UserAnswerRepository, mongo.provided.db)
# services # services
auth_service = providers.Factory(AuthService, user_repository) auth_service = providers.Factory(AuthService, user_repository)
user_service = providers.Factory(UserService, user_repository) user_service = providers.Factory(UserService, user_repository)
quiz_service = providers.Factory(QuizService, quiz_repository) quiz_service = providers.Factory(QuizService, quiz_repository)
answer_service = providers.Factory(AnswerService, answer_repository)
# controllers # controllers
auth_controller = providers.Factory(AuthController, user_service, auth_service) auth_controller = providers.Factory(AuthController, user_service, auth_service)
user_controller = providers.Factory(UserController, user_service) user_controller = providers.Factory(UserController, user_service)
quiz_controller = providers.Factory(QuizController, quiz_service) quiz_controller = providers.Factory(QuizController, quiz_service, answer_service)

View File

@ -1,5 +1,5 @@
# app/models/__init__.py # app/models/__init__.py
from .entities import UserEntity, QuizEntity, QuestionItemEntity from .entities import UserEntity, QuizEntity, QuestionItemEntity, UserAnswerEntity
from .login import UserResponseModel from .login import UserResponseModel
@ -9,4 +9,5 @@ __all__ = [
"UserResponseModel", "UserResponseModel",
"QuizEntity", "QuizEntity",
"QuestionItemEntity", "QuestionItemEntity",
"UserAnswerEntity",
] ]

View File

@ -2,10 +2,12 @@ from .user_entity import UserEntity
from .base import PyObjectId from .base import PyObjectId
from .quiz_entity import QuizEntity from .quiz_entity import QuizEntity
from .question_item_entity import QuestionItemEntity from .question_item_entity import QuestionItemEntity
from .user_answer_entity import UserAnswerEntity
__all__ = [ __all__ = [
"UserEntity", "UserEntity",
"PyObjectId", "PyObjectId",
"QuizEntity", "QuizEntity",
"QuestionItemEntity", "QuestionItemEntity",
"UserAnswerEntity",
] ]

View File

@ -0,0 +1,11 @@
from pydantic import BaseModel
class AnswerItemEntity(BaseModel):
question_index: int
question: str
answer: str
correct_answer: str
is_correct: bool
duration: int
time_spent: float

View File

@ -1,8 +1,10 @@
from bson import ObjectId from bson import ObjectId
from pydantic import GetJsonSchemaHandler
from pydantic.json_schema import JsonSchemaValue
class PyObjectId(ObjectId): class PyObjectId(ObjectId):
"""Custom ObjectId type for Pydantic to handle MongoDB _id""" """Custom ObjectId type for Pydantic v2 to handle MongoDB _id"""
@classmethod @classmethod
def __get_validators__(cls): def __get_validators__(cls):
@ -15,5 +17,7 @@ class PyObjectId(ObjectId):
return ObjectId(v) return ObjectId(v)
@classmethod @classmethod
def __modify_schema__(cls, field_schema): def __get_pydantic_json_schema__(
field_schema.update(type="string") cls, schema: JsonSchemaValue, handler: GetJsonSchemaHandler
) -> JsonSchemaValue:
return {"type": "string"}

View File

@ -0,0 +1,24 @@
from typing import List, Optional
from pydantic import BaseModel, Field
from datetime import datetime
from bson import ObjectId
from .answer_item import AnswerItemEntity
from .base import PyObjectId
class UserAnswerEntity(BaseModel):
_id: Optional[PyObjectId] = None
session_id: Optional[PyObjectId]
quiz_id: PyObjectId
user_id: str
answered_at: datetime
answers: List[AnswerItemEntity]
total_score: int
total_correct: int
total_questions: int
class Config:
allow_population_by_field_name = True
arbitrary_types_allowed = True
json_encoders = {ObjectId: str}

View File

@ -1,8 +1,9 @@
from .user_repository import UserRepository from .user_repository import UserRepository
from .quiz_repositroy import QuizRepository from .quiz_repositroy import QuizRepository
from .answer_repository import UserAnswerRepository
__all__ = [ __all__ = [
"UserRepository", "UserRepository",
"QuizRepository", "QuizRepository",
"UserAnswerRepository",
] ]

View File

@ -0,0 +1,32 @@
from pymongo.collection import Collection
from bson import ObjectId
from typing import Optional, List
from models import UserAnswerEntity
class UserAnswerRepository:
def __init__(self, db):
self.collection: Collection = db.user_answers
def create(self, answer_session: UserAnswerEntity) -> str:
data = answer_session.model_dump(by_alias=True)
result = self.collection.insert_one(data)
return str(result.inserted_id)
def get_by_id(self, id: str) -> Optional[dict]:
result = self.collection.find_one({"_id": ObjectId(id)})
return result
def get_by_user_and_quiz(self, user_id: str, quiz_id: str) -> List[dict]:
result = self.collection.find(
{"user_id": user_id, "quiz_id": ObjectId(quiz_id)}
)
return list(result)
def get_by_session(self, session_id: str) -> List[dict]:
result = self.collection.find({"session_id": ObjectId(session_id)})
return list(result)
def delete_by_id(self, id: str) -> bool:
result = self.collection.delete_one({"_id": ObjectId(id)})
return result.deleted_count > 0

View File

@ -5,9 +5,14 @@ from .quiz import (
QuizCreateSchema, QuizCreateSchema,
) )
from .answer.answer_request_schema import UserAnswerSchema
from .answer.answer_item_request_schema import AnswerItemSchema
__all__ = [ __all__ = [
"RegisterSchema", "RegisterSchema",
"QuestionItemSchema", "QuestionItemSchema",
"QuizCreateSchema", "QuizCreateSchema",
"UserAnswerSchema",
"AnswerItemSchema",
] ]

View File

@ -0,0 +1,11 @@
from pydantic import BaseModel
class AnswerItemSchema(BaseModel):
question_index: int
question: str
answer: str
correct_answer: str
is_correct: bool
duration: int
time_spent: float

View File

@ -0,0 +1,15 @@
from typing import List, Optional
from pydantic import BaseModel, Field
from datetime import datetime
from .answer_item_request_schema import AnswerItemSchema
class UserAnswerSchema(BaseModel):
session_id: Optional[str] = None
quiz_id: str
user_id: str
answered_at: datetime
total_score: int
total_correct: int
total_questions: int
answers: List[AnswerItemSchema]

View File

@ -1,10 +1,12 @@
from .auth_service import AuthService from .auth_service import AuthService
from .user_service import UserService from .user_service import UserService
from .quiz_service import QuizService from .quiz_service import QuizService
from .answer_service import AnswerService
__all__ = [ __all__ = [
"AuthService", "AuthService",
"UserService", "UserService",
"QuizService", "QuizService",
"AnswerService",
] ]

View File

@ -0,0 +1,22 @@
from repositories import UserAnswerRepository
class AnswerService:
def __init__(self, answer_repository: UserAnswerRepository):
self.answer_repository = answer_repository
def get_answer_by_id(self, answer_id):
return self.answer_repository.get_answer_by_id(answer_id)
def get_answer(self, quiz_id, user_id, session_id):
if quiz_id is not None:
return self.answer_repository
def create_answer(self, answer_data):
return self.answer_repository.create(answer_data)
def update_answer(self, answer_id, answer_data):
return self.answer_repository.update(answer_id, answer_data)
def delete_answer(self, answer_id):
return self.answer_repository.delete_by_id(answer_id)

View File

@ -23,3 +23,10 @@ class QuizService:
def delete_quiz(self, quiz_id): def delete_quiz(self, quiz_id):
return self.quiz_repository.delete(quiz_id) return self.quiz_repository.delete(quiz_id)
def quiz_recommendation(self):
data = self.quiz_repository
if data is None:
raise DataNotFoundException("Quiz not found")
return map_quiz_entity_to_schema(data)