From a797061cce9193ccfc2b9242141c3efff4bc2aea Mon Sep 17 00:00:00 2001 From: akhdanre Date: Sun, 27 Apr 2025 23:00:33 +0700 Subject: [PATCH] fix: user id data not showing --- app/blueprints/quiz.py | 4 +-- app/main.py | 19 ++++++++--- app/mapper/user_mapper.py | 3 +- app/models/entities/base.py | 32 +++++++++++-------- app/models/entities/user_answer_entity.py | 2 +- app/models/entities/user_entity.py | 10 ++++-- app/models/login/login_response.py | 2 +- app/repositories/user_repository.py | 19 ++++++----- .../requests/quiz/create_quiz_schema.py | 2 +- app/schemas/requests/quiz/quiz_item_schema.py | 2 ++ app/services/auth_service.py | 4 +-- 11 files changed, 61 insertions(+), 38 deletions(-) diff --git a/app/blueprints/quiz.py b/app/blueprints/quiz.py index 76d1da8..3c201e1 100644 --- a/app/blueprints/quiz.py +++ b/app/blueprints/quiz.py @@ -4,10 +4,10 @@ from dependency_injector.wiring import inject, Provide from controllers import QuizController -quiz_bp = Blueprint("quiz", __name__, url_prefix="/quiz") +quiz_bp = Blueprint("quiz", __name__) -@quiz_bp.route("/", methods=["POST"]) +@quiz_bp.route("", methods=["POST"]) @inject def create_quiz(controller: QuizController = Provide[Container.quiz_controller]): reqBody = request.get_json() diff --git a/app/main.py b/app/main.py index 853ed2b..b6aa521 100644 --- a/app/main.py +++ b/app/main.py @@ -23,15 +23,26 @@ def createApp() -> Flask: if mongo is not None: container.mongo.override(mongo) - container.wire(modules=["blueprints.auth"]) - container.wire(modules=["blueprints.user"]) - container.wire(modules=["blueprints.quiz"]) + # container.wire(modules=["blueprints.auth"]) + # container.wire(modules=["blueprints.user"]) + # container.wire(modules=["blueprints.quiz"]) + + container.wire( + modules=[ + "blueprints.auth", + "blueprints.user", + "blueprints.quiz", + ] + ) # Register Blueprints app.register_blueprint(default_blueprint) app.register_blueprint(auth_blueprint, url_prefix="/api") app.register_blueprint(user_blueprint, url_prefix="/api") - app.register_blueprint(quiz_bp, url_prefix="/api") + app.register_blueprint(quiz_bp, url_prefix="/api/quiz") + + # for rule in app.url_map.iter_rules(): + # print(f"Route: {rule} -> Methods: {rule.methods}") return app diff --git a/app/mapper/user_mapper.py b/app/mapper/user_mapper.py index 4b2c835..13208c8 100644 --- a/app/mapper/user_mapper.py +++ b/app/mapper/user_mapper.py @@ -42,8 +42,9 @@ class UserMapper: @staticmethod def user_entity_to_response(user: UserEntity) -> UserResponseModel: + print(user.id) return UserResponseModel( - _id=str(user._id) if user._id else None, + id=str(user.id) if user.id else None, google_id=user.google_id, email=user.email, name=user.name, diff --git a/app/models/entities/base.py b/app/models/entities/base.py index 382e102..dc7540d 100644 --- a/app/models/entities/base.py +++ b/app/models/entities/base.py @@ -1,23 +1,29 @@ from bson import ObjectId -from pydantic import GetJsonSchemaHandler -from pydantic.json_schema import JsonSchemaValue +from pydantic import GetCoreSchemaHandler +from pydantic_core import core_schema class PyObjectId(ObjectId): """Custom ObjectId type for Pydantic v2 to handle MongoDB _id""" @classmethod - def __get_validators__(cls): - yield cls.validate + def __get_pydantic_core_schema__( + cls, source, handler: GetCoreSchemaHandler + ) -> core_schema.CoreSchema: + return core_schema.no_info_after_validator_function( + cls.validate, + core_schema.union_schema( + [ + core_schema.str_schema(), + core_schema.is_instance_schema(ObjectId), + ] + ), + ) @classmethod def validate(cls, v): - if not ObjectId.is_valid(v): - raise ValueError("Invalid ObjectId") - return ObjectId(v) - - @classmethod - def __get_pydantic_json_schema__( - cls, schema: JsonSchemaValue, handler: GetJsonSchemaHandler - ) -> JsonSchemaValue: - return {"type": "string"} + if isinstance(v, ObjectId): + return v + if isinstance(v, str) and ObjectId.is_valid(v): + return ObjectId(v) + raise ValueError(f"Invalid ObjectId: {v}") diff --git a/app/models/entities/user_answer_entity.py b/app/models/entities/user_answer_entity.py index 8128ee2..90aa669 100644 --- a/app/models/entities/user_answer_entity.py +++ b/app/models/entities/user_answer_entity.py @@ -19,6 +19,6 @@ class UserAnswerEntity(BaseModel): total_questions: int class Config: - allow_population_by_field_name = True + populate_by_name = True arbitrary_types_allowed = True json_encoders = {ObjectId: str} diff --git a/app/models/entities/user_entity.py b/app/models/entities/user_entity.py index 926ec8d..96a65b0 100644 --- a/app/models/entities/user_entity.py +++ b/app/models/entities/user_entity.py @@ -1,13 +1,13 @@ from typing import Optional -from pydantic import BaseModel, EmailStr +from pydantic import BaseModel, Field from datetime import datetime from .base import PyObjectId class UserEntity(BaseModel): - _id: Optional[PyObjectId] = None + id: Optional[PyObjectId] = Field(default=None, alias="_id") google_id: Optional[str] = None - email: EmailStr + email: str password: Optional[str] = None name: str birth_date: Optional[datetime] = None @@ -16,3 +16,7 @@ class UserEntity(BaseModel): locale: str = "en-US" created_at: Optional[datetime] = None updated_at: Optional[datetime] = None + + class Config: + populate_by_name = True + json_encoders = {PyObjectId: str} diff --git a/app/models/login/login_response.py b/app/models/login/login_response.py index b31ce3a..dff46ee 100644 --- a/app/models/login/login_response.py +++ b/app/models/login/login_response.py @@ -14,7 +14,7 @@ class UserResponseModel(BaseModel): locale: str class Config: - allow_population_by_field_name = True + populate_by_name = True json_encoders = { datetime: lambda v: v.isoformat(), } diff --git a/app/repositories/user_repository.py b/app/repositories/user_repository.py index 2d89941..edf3d08 100644 --- a/app/repositories/user_repository.py +++ b/app/repositories/user_repository.py @@ -4,44 +4,43 @@ from models import UserEntity class UserRepository: - def __init__(self, db): self.collection = db.users def get_all_users(self) -> list[UserEntity]: - """Mengambil semua user dari database.""" + """Retrieve all users from the database.""" users = list(self.collection.find({}, {"_id": 0})) return [UserEntity(**user) for user in users] def get_user_by_email(self, email: str) -> Optional[UserEntity]: - """Mendapatkan user berdasarkan email.""" - user = self.collection.find_one({"email": email}, {"_id": 0}) + """Retrieve a user based on their email address.""" + user = self.collection.find_one({"email": email}) return UserEntity(**user) if user else None def get_user_by_id(self, user_id: str) -> Optional[UserEntity]: - """Mendapatkan user berdasarkan ID.""" + """Retrieve a user based on their ID.""" object_id = ObjectId(user_id) user = self.collection.find_one({"_id": object_id}) return UserEntity(**user) if user else None def get_by_google_id(self, google_id: str) -> Optional[UserEntity]: + """Retrieve a user based on their Google ID.""" user_data = self.collection.find_one({"google_id": google_id}) - return UserEntity(**user_data) if user_data else None def insert_user(self, user_data: UserEntity) -> str: - """Menambahkan pengguna baru ke dalam database dan mengembalikan ID pengguna.""" + """Insert a new user into the database and return the user's ID.""" result = self.collection.insert_one(user_data.model_dump()) return str(result.inserted_id) def update_user(self, user_id: str, update_data: dict) -> bool: - """Mengupdate seluruh data user berdasarkan ID.""" + """Update all fields of a user based on their ID.""" object_id = ObjectId(user_id) result = self.collection.update_one({"_id": object_id}, {"$set": update_data}) return result.modified_count > 0 def update_user_field(self, user_id: str, field: str, value) -> bool: - """Mengupdate satu field dari user berdasarkan ID.""" + """Update a single field of a user based on their ID.""" object_id = ObjectId(user_id) result = self.collection.update_one( {"_id": object_id}, {"$set": {field: value}} @@ -49,7 +48,7 @@ class UserRepository: return result.modified_count > 0 def delete_user(self, user_id: str) -> bool: - """Menghapus user berdasarkan ID.""" + """Delete a user based on their ID.""" object_id = ObjectId(user_id) result = self.collection.delete_one({"_id": object_id}) return result.deleted_count > 0 diff --git a/app/schemas/requests/quiz/create_quiz_schema.py b/app/schemas/requests/quiz/create_quiz_schema.py index 5c8e76d..7f93132 100644 --- a/app/schemas/requests/quiz/create_quiz_schema.py +++ b/app/schemas/requests/quiz/create_quiz_schema.py @@ -8,7 +8,7 @@ class QuizCreateSchema(BaseModel): title: str description: Optional[str] = None is_public: bool = False - date: Optional[datetime] = None + date: Optional[str] = None total_quiz: Optional[int] = 0 limit_duration: Optional[int] = 0 author_id: Optional[str] = None diff --git a/app/schemas/requests/quiz/quiz_item_schema.py b/app/schemas/requests/quiz/quiz_item_schema.py index 6f98a7b..641a923 100644 --- a/app/schemas/requests/quiz/quiz_item_schema.py +++ b/app/schemas/requests/quiz/quiz_item_schema.py @@ -1,3 +1,4 @@ +from typing import List, Optional from pydantic import BaseModel @@ -6,3 +7,4 @@ class QuestionItemSchema(BaseModel): target_answer: str duration: int type: str + options: Optional[List[str]] = None diff --git a/app/services/auth_service.py b/app/services/auth_service.py index c6f0ff0..f40e46e 100644 --- a/app/services/auth_service.py +++ b/app/services/auth_service.py @@ -37,10 +37,10 @@ class AuthService: def login(self, data: LoginSchema): - current_app.logger.info(f"request data: {data}") + # current_app.logger.info(f"request data: {data}") user_data = self.user_repository.get_user_by_email(data.email) - current_app.logger.info(f"user_data: {user_data}") + # current_app.logger.info(f"user_data: {user_data}") if user_data == None: return None