fix: response properties

This commit is contained in:
akhdanre 2025-05-04 01:10:18 +07:00
parent 5774850aaa
commit 3c0d8e1e15
12 changed files with 91 additions and 33 deletions

View File

@ -50,6 +50,7 @@ class Container(containers.DeclarativeContainer):
QuizService, QuizService,
quiz_repository, quiz_repository,
user_repository, user_repository,
subject_repository,
) )
answer_service = providers.Factory( answer_service = providers.Factory(
AnswerService, AnswerService,

View File

@ -1,6 +1,7 @@
from datetime import datetime from datetime import datetime
from helpers import DatetimeUtil from helpers import DatetimeUtil
from models import QuizEntity, QuestionItemEntity, UserEntity from models import QuizEntity, QuestionItemEntity, UserEntity
from models.entities import SubjectEntity
from schemas import QuizGetSchema, QuestionItemSchema from schemas import QuizGetSchema, QuestionItemSchema
from schemas.response import RecomendationResponse from schemas.response import RecomendationResponse
from schemas.requests import QuizCreateSchema from schemas.requests import QuizCreateSchema
@ -31,10 +32,15 @@ class QuizMapper:
) )
@staticmethod @staticmethod
def map_quiz_entity_to_schema(entity: QuizEntity) -> QuizGetSchema: def map_quiz_entity_to_schema(
entity: QuizEntity,
subjectE: SubjectEntity,
) -> QuizGetSchema:
return QuizGetSchema( return QuizGetSchema(
id=str(entity.id), id=str(entity.id),
author_id=entity.author_id, author_id=entity.author_id,
subject_id=str(subjectE.id),
subject_alias=subjectE.short_name,
title=entity.title, title=entity.title,
description=entity.description, description=entity.description,
is_public=entity.is_public, is_public=entity.is_public,
@ -56,6 +62,7 @@ class QuizMapper:
) -> QuizEntity: ) -> QuizEntity:
return QuizEntity( return QuizEntity(
author_id=schema.author_id, author_id=schema.author_id,
subject_id=schema.subject_id,
title=schema.title, title=schema.title,
description=schema.description, description=schema.description,
is_public=schema.is_public, is_public=schema.is_public,

View File

@ -1,8 +1,9 @@
from pydantic import BaseModel from pydantic import BaseModel
from typing import Union
class AnswerItemEntity(BaseModel): class AnswerItemEntity(BaseModel):
question_index: int question_index: int
answer: str answer: Union[str | int | bool]
is_correct: bool is_correct: bool
time_spent: float time_spent: float

View File

@ -8,9 +8,9 @@ from .question_item_entity import QuestionItemEntity
class QuizEntity(BaseModel): class QuizEntity(BaseModel):
id: Optional[PyObjectId] = Field(default=None, alias="_id") id: Optional[PyObjectId] = Field(default=None, alias="_id")
author_id: Optional[str] = None author_id: Optional[str] = None
subject_id: str
title: str title: str
description: Optional[str] = None description: Optional[str] = None
# subject: str
is_public: bool = False is_public: bool = False
date: datetime date: datetime
total_quiz: int = 0 total_quiz: int = 0

View File

@ -1,35 +1,61 @@
from typing import List, Optional from typing import List, Optional
from pymongo.database import Database from pymongo.database import Database
from pymongo.collection import Collection from pymongo.collection import Collection
from bson import ObjectId, errors as bson_errors
from models.entities import SubjectEntity from models.entities import SubjectEntity
from bson import ObjectId
class SubjectRepository: class SubjectRepository:
COLLECTION_NAME = "subjects"
def __init__(self, db: Database): def __init__(self, db: Database):
self.collection: Collection = db.subjects self.collection: Collection = db[self.COLLECTION_NAME]
def create(self, subject: SubjectEntity) -> str: def create(self, subject: SubjectEntity) -> str:
subject_dict = subject.dict(by_alias=True, exclude_none=True) subject_dict = subject.model_dump(by_alias=True, exclude_none=True)
result = self.collection.insert_one(subject_dict) result = self.collection.insert_one(subject_dict)
return str(result.inserted_id) return str(result.inserted_id)
def get_all(self) -> List[SubjectEntity]: def get_all(self) -> List[SubjectEntity]:
cursor = self.collection.find({}) return [SubjectEntity(**doc) for doc in self.collection.find()]
return [SubjectEntity(**doc) for doc in cursor]
def get_by_id(self, subject_id: str) -> Optional[SubjectEntity]: def get_by_id(self, subject_id: str) -> Optional[SubjectEntity]:
doc = self.collection.find_one({"_id": ObjectId(subject_id)}) try:
if doc: oid = ObjectId(subject_id)
return SubjectEntity(**doc) except bson_errors.InvalidId:
return None return None
doc = self.collection.find_one({"_id": oid})
return SubjectEntity(**doc) if doc else None
def get_by_ids(self, subject_ids: List[str]) -> List[SubjectEntity]:
object_ids = []
for sid in subject_ids:
try:
object_ids.append(ObjectId(sid))
except bson_errors.InvalidId:
continue
if not object_ids:
return []
cursor = self.collection.find({"_id": {"$in": object_ids}})
return [SubjectEntity(**doc) for doc in cursor]
def update(self, subject_id: str, update_data: dict) -> bool: def update(self, subject_id: str, update_data: dict) -> bool:
result = self.collection.update_one( try:
{"_id": ObjectId(subject_id)}, {"$set": update_data} oid = ObjectId(subject_id)
) except bson_errors.InvalidId:
return False
result = self.collection.update_one({"_id": oid}, {"$set": update_data})
return result.modified_count > 0 return result.modified_count > 0
def delete(self, subject_id: str) -> bool: def delete(self, subject_id: str) -> bool:
result = self.collection.delete_one({"_id": ObjectId(subject_id)}) try:
oid = ObjectId(subject_id)
except bson_errors.InvalidId:
return False
result = self.collection.delete_one({"_id": oid})
return result.deleted_count > 0 return result.deleted_count > 0

View File

@ -1,8 +1,9 @@
from pydantic import BaseModel from pydantic import BaseModel
from typing import Union
class AnswerItemSchema(BaseModel): class AnswerItemSchema(BaseModel):
question_index: int question_index: int
answer: str answer: Union[str | int | bool]
is_correct: bool is_correct: bool
time_spent: float time_spent: float

View File

@ -8,5 +8,6 @@ class QuizCreateSchema(BaseModel):
title: str title: str
description: Optional[str] = None description: Optional[str] = None
is_public: bool = False is_public: bool = False
subject_id: str
author_id: Optional[str] = None author_id: Optional[str] = None
question_listings: Optional[List[QuestionItemSchema]] = [] question_listings: Optional[List[QuestionItemSchema]] = []

View File

@ -1,4 +1,4 @@
from typing import List, Optional from typing import List, Optional, Union
from pydantic import BaseModel from pydantic import BaseModel
from datetime import datetime from datetime import datetime
@ -7,8 +7,8 @@ class QuestionResult(BaseModel):
index: int index: int
question: str question: str
type: str type: str
target_answer: str target_answer: Union[str | bool | int]
user_answer: Optional[str] user_answer: Optional[Union[str | bool | int]]
is_correct: Optional[bool] is_correct: Optional[bool]
time_spent: Optional[float] time_spent: Optional[float]
options: Optional[List[str]] options: Optional[List[str]]

View File

@ -1,8 +1,10 @@
from typing import List from typing import List
from pydantic import BaseModel from pydantic import BaseModel
from schemas.response import QuizGetSchema from schemas.response.recomendation.recomendation_response_schema import (
RecomendationResponse,
)
class UserQuizListResponse(BaseModel): class UserQuizListResponse(BaseModel):
total: int total: int
quizzes: List[QuizGetSchema] quizzes: List[RecomendationResponse]

View File

@ -7,6 +7,8 @@ from .question_item_schema import QuestionItemSchema
class QuizGetSchema(BaseModel): class QuizGetSchema(BaseModel):
id: str id: str
author_id: str author_id: str
subject_id: str
subject_alias: str
title: str title: str
description: Optional[str] = None description: Optional[str] = None
is_public: bool = False is_public: bool = False

View File

@ -47,11 +47,13 @@ class AnswerService:
) )
correct = False correct = False
if question.type in ["fill_the_blank", "true_false"]: if question.type == "fill_the_blank":
correct = ( correct = (
user_answer.answer.strip().lower() user_answer.answer.strip().lower()
== question.target_answer.strip().lower() == question.target_answer.strip().lower()
) )
elif question.type == "true_false":
correct = user_answer.answer == question.target_answer
elif question.type == "option": elif question.type == "option":
try: try:
answer_index = int(user_answer.answer) answer_index = int(user_answer.answer)

View File

@ -1,8 +1,6 @@
from models import QuizEntity from repositories import QuizRepository, UserRepository, SubjectRepository
from repositories import QuizRepository, UserRepository
from schemas import QuizGetSchema
from schemas.requests import QuizCreateSchema from schemas.requests import QuizCreateSchema
from schemas.response import UserQuizListResponse, RecomendationResponse from schemas.response import UserQuizListResponse, RecomendationResponse, QuizGetSchema
from exception import DataNotFoundException from exception import DataNotFoundException
from mapper import QuizMapper from mapper import QuizMapper
from exception import ValidationException from exception import ValidationException
@ -10,15 +8,23 @@ from helpers import DatetimeUtil
class QuizService: class QuizService:
def __init__(self, quiz_repository=QuizRepository, user_repository=UserRepository): def __init__(
self,
quiz_repository=QuizRepository,
user_repository=UserRepository,
subject_repository=SubjectRepository,
):
self.quiz_repository = quiz_repository self.quiz_repository = quiz_repository
self.user_repostory = user_repository self.user_repostory = user_repository
self.subject_repository = subject_repository
def get_quiz(self, quiz_id) -> QuizGetSchema: def get_quiz(self, quiz_id) -> QuizGetSchema:
data = self.quiz_repository.get_by_id(quiz_id) data = self.quiz_repository.get_by_id(quiz_id)
if data is None: if data is None:
raise DataNotFoundException("Quiz not found") raise DataNotFoundException("Quiz not found")
return QuizMapper.map_quiz_entity_to_schema(data) quiz_subject = self.subject_repository.get_by_id(data.subject_id)
return QuizMapper.map_quiz_entity_to_schema(data, quiz_subject)
def search_quiz( def search_quiz(
self, keyword: str, page: int = 1, page_size: int = 10 self, keyword: str, page: int = 1, page_size: int = 10
@ -46,11 +52,20 @@ class QuizService:
self, user_id: str, page: int = 1, page_size: int = 10 self, user_id: str, page: int = 1, page_size: int = 10
) -> UserQuizListResponse: ) -> UserQuizListResponse:
quizzes = self.quiz_repository.get_by_user_id(user_id, page, page_size) quizzes = self.quiz_repository.get_by_user_id(user_id, page, page_size)
if not quizzes:
return UserQuizListResponse(total=0, quizzes=[])
total_user_quiz = self.quiz_repository.count_by_user_id(user_id) total_user_quiz = self.quiz_repository.count_by_user_id(user_id)
return UserQuizListResponse(
total=total_user_quiz, print(total_user_quiz)
quizzes=[QuizMapper.map_quiz_entity_to_schema(quiz) for quiz in quizzes],
) user = self.user_repostory.get_user_by_id(user_id)
quiz_data = [
QuizMapper.quiz_to_recomendation_mapper(quiz, user) for quiz in quizzes
]
return UserQuizListResponse(total=total_user_quiz, quizzes=quiz_data)
def create_quiz(self, quiz_data: QuizCreateSchema): def create_quiz(self, quiz_data: QuizCreateSchema):
total_time = 0 total_time = 0