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
quiz_bp = Blueprint(
"quiz",
__name__,
)
quiz_bp = Blueprint("quiz", __name__, url_prefix="/quiz")
@quiz_bp.route("/quiz", methods=["POST"])
@quiz_bp.route("/", methods=["POST"])
@inject
def create_quiz(controller: QuizController = Provide[Container.quiz_controller]):
reqBody = request.get_json()
return controller.create_quiz(reqBody)
@quiz_bp.route("/quiz/<quiz_id>", methods=["GET"])
@quiz_bp.route("/<quiz_id>", methods=["GET"])
@inject
def get_quiz(
quiz_id: str, controller: QuizController = Provide[Container.quiz_controller]
):
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 pydantic import ValidationError
from schemas.requests import QuizCreateSchema
from schemas.requests import QuizCreateSchema, UserAnswerSchema
from schemas.response import QuizCreationResponse
from services import QuizService
from services import QuizService, AnswerService
from helpers import make_response, make_error_response
class QuizController:
def __init__(self, quiz_service: QuizService):
def __init__(self, quiz_service: QuizService, answer_service: AnswerService):
self.quiz_service = quiz_service
self.answer_service = answer_service
def get_quiz(self, quiz_id):
try:
@ -50,3 +51,34 @@ class QuizController:
if not success:
return jsonify({"error": "Quiz not found"}), 400
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 controllers import UserController
from repositories.user_repository import UserRepository
from services import UserService, AuthService
from controllers import AuthController
from flask_pymongo import PyMongo
from repositories import QuizRepository
from services import QuizService
from controllers import QuizController
from controllers import UserController, AuthController, QuizController
from repositories import UserRepository, QuizRepository, UserAnswerRepository
from services import UserService, AuthService, QuizService, AnswerService
class Container(containers.DeclarativeContainer):
@ -17,13 +12,15 @@ class Container(containers.DeclarativeContainer):
# repository
user_repository = providers.Factory(UserRepository, mongo.provided.db)
quiz_repository = providers.Factory(QuizRepository, mongo.provided.db)
answer_repository = providers.Factory(UserAnswerRepository, mongo.provided.db)
# services
auth_service = providers.Factory(AuthService, user_repository)
user_service = providers.Factory(UserService, user_repository)
quiz_service = providers.Factory(QuizService, quiz_repository)
answer_service = providers.Factory(AnswerService, answer_repository)
# controllers
auth_controller = providers.Factory(AuthController, user_service, auth_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
from .entities import UserEntity, QuizEntity, QuestionItemEntity
from .entities import UserEntity, QuizEntity, QuestionItemEntity, UserAnswerEntity
from .login import UserResponseModel
@ -9,4 +9,5 @@ __all__ = [
"UserResponseModel",
"QuizEntity",
"QuestionItemEntity",
"UserAnswerEntity",
]

View File

@ -2,10 +2,12 @@ from .user_entity import UserEntity
from .base import PyObjectId
from .quiz_entity import QuizEntity
from .question_item_entity import QuestionItemEntity
from .user_answer_entity import UserAnswerEntity
__all__ = [
"UserEntity",
"PyObjectId",
"QuizEntity",
"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 pydantic import GetJsonSchemaHandler
from pydantic.json_schema import JsonSchemaValue
class PyObjectId(ObjectId):
"""Custom ObjectId type for Pydantic to handle MongoDB _id"""
"""Custom ObjectId type for Pydantic v2 to handle MongoDB _id"""
@classmethod
def __get_validators__(cls):
@ -15,5 +17,7 @@ class PyObjectId(ObjectId):
return ObjectId(v)
@classmethod
def __modify_schema__(cls, field_schema):
field_schema.update(type="string")
def __get_pydantic_json_schema__(
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 .quiz_repositroy import QuizRepository
from .answer_repository import UserAnswerRepository
__all__ = [
"UserRepository",
"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,
)
from .answer.answer_request_schema import UserAnswerSchema
from .answer.answer_item_request_schema import AnswerItemSchema
__all__ = [
"RegisterSchema",
"QuestionItemSchema",
"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 .user_service import UserService
from .quiz_service import QuizService
from .answer_service import AnswerService
__all__ = [
"AuthService",
"UserService",
"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):
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)