From 6bb7453539c2cba7e3db364033e1f33523ced88c Mon Sep 17 00:00:00 2001 From: pradanaadn Date: Tue, 9 Dec 2025 20:36:14 +0700 Subject: [PATCH 1/6] chore: add initial data for checkin seeder --- cli.py | 8 ++++ seeders/initial_checkin_data.py | 84 +++++++++++++++++++++++++++++++++ 2 files changed, 92 insertions(+) create mode 100644 seeders/initial_checkin_data.py diff --git a/cli.py b/cli.py index 3bf64bc..44f795d 100644 --- a/cli.py +++ b/cli.py @@ -79,5 +79,13 @@ def clear_location_data(): clear_data_location(db=session, is_commit=True) +@app.command() +def checkin_example_data(): + from models import factory_session + from seeders.initial_checkin_data import initialize_checkin_data + + with factory_session() as session: + initialize_checkin_data(db=session) + if __name__ == "__main__": app() diff --git a/seeders/initial_checkin_data.py b/seeders/initial_checkin_data.py new file mode 100644 index 0000000..8199076 --- /dev/null +++ b/seeders/initial_checkin_data.py @@ -0,0 +1,84 @@ +from sqlalchemy.orm import Session +from sqlalchemy import select + +def initialize_checkin_data( + db: Session, + +): + import uuid + + from models.User import User + from models.Ticket import Ticket + from models.Payment import Payment, PaymentStatus + from core.security import generate_hash_password + from core.helper import get_current_time_in_timezone + + ticket_id = uuid.UUID("550e8400-e29b-41d4-a716-446655440000") + payment_id = uuid.UUID("550e8400-e29b-41d4-a716-446655440001") + user_id = uuid.UUID("550e8400-e29b-41d4-a716-446655440002") + + # Check if data already exists + existing_user = db.execute(select(User).where(User.id == user_id)).scalar_one_or_none() + existing_ticket = db.execute(select(Ticket).where(Ticket.id == ticket_id)).scalar_one_or_none() + existing_payment = db.execute(select(Payment).where(Payment.id == payment_id)).scalar_one_or_none() + + if existing_user and existing_ticket and existing_payment: + print("All checkin data already exists. Skipping...") + return + + try: + if not existing_user: + user = User( + id=user_id, + username="checkinuser", + email="checkinuser@example.com", + phone="+628123456789", + first_name="test", + last_name="user", + password=generate_hash_password("password"), + is_active=True, + ) + db.add(user) + else: + user = existing_user + + if not existing_ticket: + ticket = Ticket( + id=ticket_id, + name="Test Conference Ticket", + price=500000, + user_participant_type="In Person", + is_sold_out=False, + is_active=True, + description="Test ticket for payment", + ) + db.add(ticket) + else: + ticket = existing_ticket + + if not existing_payment: + payment = Payment( + id=payment_id, + user_id=user.id, + ticket_id=ticket.id, + payment_link="https://mayar.id/pay/test-link", + status=PaymentStatus.PAID, + created_at=get_current_time_in_timezone(), + mayar_id="mayar-test-id", + mayar_transaction_id="mayar-test-tx", + amount=500000, + description="Test payment", + ) + db.add(payment) + else: + payment = existing_payment + + db.commit() + db.refresh(user) + db.refresh(ticket) + db.refresh(payment) + print("Checkin data initialized successfully!") + except Exception as e: + db.rollback() + print(f"Error initializing checkin data: {e}") + raise \ No newline at end of file From 84f866f3f6242380b3399af06268ab1fe01f6fb2 Mon Sep 17 00:00:00 2001 From: pradanaadn Date: Tue, 9 Dec 2025 20:36:29 +0700 Subject: [PATCH 2/6] fix: add uuid validator --- repository/checkin.py | 9 ++++++++- 1 file changed, 8 insertions(+), 1 deletion(-) diff --git a/repository/checkin.py b/repository/checkin.py index cce069f..1035be1 100644 --- a/repository/checkin.py +++ b/repository/checkin.py @@ -1,6 +1,6 @@ import datetime from zoneinfo import ZoneInfo, ZoneInfoNotFoundError - +from uuid import UUID from sqlalchemy import select from sqlalchemy.orm import Session from core.log import logger @@ -21,6 +21,10 @@ def get_user_data_by_payment_id(db: Session, payment_id: str) -> User | None: Returns: User | None: User data or None if not found """ + try: + UUID(payment_id) + except ValueError: + return None query = ( select(User) .join(Payment, Payment.user_id == User.id) @@ -102,3 +106,6 @@ def set_user_checkin_status( logger.error(f"Error setting check-in status: {e}") db.rollback() return None + + + From b5e818c1654183c75d930f29d8c8a1e998b07e79 Mon Sep 17 00:00:00 2001 From: pradanaadn Date: Tue, 9 Dec 2025 20:36:51 +0700 Subject: [PATCH 3/6] chore: add updated response schema --- routes/ticket.py | 10 +++++----- schemas/checkin.py | 5 ++++- 2 files changed, 9 insertions(+), 6 deletions(-) diff --git a/routes/ticket.py b/routes/ticket.py index 3f10c1a..2830f8d 100644 --- a/routes/ticket.py +++ b/routes/ticket.py @@ -1,5 +1,4 @@ import traceback - from fastapi import APIRouter, Depends, HTTPException, Request from pydantic import ValidationError from sqlalchemy.orm import Session @@ -25,7 +24,7 @@ from repository.ticket import get_active_tickets from schemas.checkin import ( CheckinUserRequest, - CheckinUserResponse, + CheckinUserResponseSchema, user_model_to_checkin_response, ) from schemas.common import ( @@ -145,7 +144,7 @@ async def get_my_ticket( @router.get( "/checkin/{payment_id}", responses={ - "200": {"model": CheckinUserResponse}, + "200": {"model": CheckinUserResponseSchema}, "404": {"model": NotFoundResponse}, "500": {"model": InternalServerErrorResponse}, }, @@ -161,6 +160,7 @@ async def checkin(payment_id: str, db: Session = Depends(get_db_sync)): CheckinUserResponse: User check-in data """ try: + result = get_user_data_by_payment_id(db, payment_id) if result is None: return common_response( @@ -192,7 +192,7 @@ async def checkin(payment_id: str, db: Session = Depends(get_db_sync)): @router.patch( "/checkin", responses={ - "200": {"model": CheckinUserResponse}, + "200": {"model": CheckinUserResponseSchema}, "401": {"model": UnauthorizedResponse}, "402": {"model": PaymentRequiredResponse}, "404": {"model": NotFoundResponse}, @@ -259,7 +259,7 @@ async def checkin_user( @router.patch( "/checkin/reset", responses={ - "200": {"model": CheckinUserResponse}, + "200": {"model": CheckinUserResponseSchema}, "401": {"model": UnauthorizedResponse}, "402": {"model": PaymentRequiredResponse}, "404": {"model": NotFoundResponse}, diff --git a/schemas/checkin.py b/schemas/checkin.py index c9a8f92..7f89558 100644 --- a/schemas/checkin.py +++ b/schemas/checkin.py @@ -16,7 +16,10 @@ class CheckinUserResponse(BaseModel): checked_in_day1: bool = False checked_in_day2: bool = False - +class CheckinUserResponseSchema(BaseModel): + data: CheckinUserResponse + message: str = "User check-in data retrieved successfully" + def user_model_to_checkin_response(user: User) -> CheckinUserResponse: """Convert User model to CheckinUserResponse schema From e8801e526a05e1e0cb7ee5672580335a04de8a69 Mon Sep 17 00:00:00 2001 From: pradanaadn Date: Tue, 9 Dec 2025 20:37:22 +0700 Subject: [PATCH 4/6] feat: update the test, remove 500 in status code for not found --- routes/tests/test_checkin.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/routes/tests/test_checkin.py b/routes/tests/test_checkin.py index 5686008..8e20d76 100644 --- a/routes/tests/test_checkin.py +++ b/routes/tests/test_checkin.py @@ -132,7 +132,7 @@ def test_get_user_data_by_payment_id_not_found(self): def test_get_user_data_by_payment_id_invalid(self): """Test response when payment ID format is invalid""" response = self.client.get("/ticket/checkin/invalid-uuid-format") - self.assertIn(response.status_code, [404, 500]) + self.assertEqual(response.status_code, 404) def test_get_user_data_with_no_tshirt_size(self): """Test retrieval when user has no t-shirt size set""" From 31a5895472446680bb377e5c1ccb90ecead2935d Mon Sep 17 00:00:00 2001 From: pradanaadn Date: Tue, 9 Dec 2025 20:37:43 +0700 Subject: [PATCH 5/6] chore: ruff format --- cli.py | 3 +- repository/checkin.py | 3 -- routes/ticket.py | 1 - schemas/checkin.py | 4 +- seeders/initial_checkin_data.py | 86 ++++++++++++++++++--------------- 5 files changed, 51 insertions(+), 46 deletions(-) diff --git a/cli.py b/cli.py index 44f795d..4fbb2a3 100644 --- a/cli.py +++ b/cli.py @@ -83,9 +83,10 @@ def clear_location_data(): def checkin_example_data(): from models import factory_session from seeders.initial_checkin_data import initialize_checkin_data - + with factory_session() as session: initialize_checkin_data(db=session) + if __name__ == "__main__": app() diff --git a/repository/checkin.py b/repository/checkin.py index 1035be1..27ca263 100644 --- a/repository/checkin.py +++ b/repository/checkin.py @@ -106,6 +106,3 @@ def set_user_checkin_status( logger.error(f"Error setting check-in status: {e}") db.rollback() return None - - - diff --git a/routes/ticket.py b/routes/ticket.py index 2830f8d..246bf44 100644 --- a/routes/ticket.py +++ b/routes/ticket.py @@ -160,7 +160,6 @@ async def checkin(payment_id: str, db: Session = Depends(get_db_sync)): CheckinUserResponse: User check-in data """ try: - result = get_user_data_by_payment_id(db, payment_id) if result is None: return common_response( diff --git a/schemas/checkin.py b/schemas/checkin.py index 7f89558..c2711e0 100644 --- a/schemas/checkin.py +++ b/schemas/checkin.py @@ -16,10 +16,12 @@ class CheckinUserResponse(BaseModel): checked_in_day1: bool = False checked_in_day2: bool = False + class CheckinUserResponseSchema(BaseModel): data: CheckinUserResponse message: str = "User check-in data retrieved successfully" - + + def user_model_to_checkin_response(user: User) -> CheckinUserResponse: """Convert User model to CheckinUserResponse schema diff --git a/seeders/initial_checkin_data.py b/seeders/initial_checkin_data.py index 8199076..dec1e3a 100644 --- a/seeders/initial_checkin_data.py +++ b/seeders/initial_checkin_data.py @@ -1,9 +1,9 @@ from sqlalchemy.orm import Session from sqlalchemy import select + def initialize_checkin_data( db: Session, - ): import uuid @@ -12,67 +12,73 @@ def initialize_checkin_data( from models.Payment import Payment, PaymentStatus from core.security import generate_hash_password from core.helper import get_current_time_in_timezone - + ticket_id = uuid.UUID("550e8400-e29b-41d4-a716-446655440000") payment_id = uuid.UUID("550e8400-e29b-41d4-a716-446655440001") user_id = uuid.UUID("550e8400-e29b-41d4-a716-446655440002") - + # Check if data already exists - existing_user = db.execute(select(User).where(User.id == user_id)).scalar_one_or_none() - existing_ticket = db.execute(select(Ticket).where(Ticket.id == ticket_id)).scalar_one_or_none() - existing_payment = db.execute(select(Payment).where(Payment.id == payment_id)).scalar_one_or_none() - + existing_user = db.execute( + select(User).where(User.id == user_id) + ).scalar_one_or_none() + existing_ticket = db.execute( + select(Ticket).where(Ticket.id == ticket_id) + ).scalar_one_or_none() + existing_payment = db.execute( + select(Payment).where(Payment.id == payment_id) + ).scalar_one_or_none() + if existing_user and existing_ticket and existing_payment: print("All checkin data already exists. Skipping...") return - + try: if not existing_user: user = User( - id=user_id, - username="checkinuser", - email="checkinuser@example.com", - phone="+628123456789", - first_name="test", - last_name="user", - password=generate_hash_password("password"), - is_active=True, - ) + id=user_id, + username="checkinuser", + email="checkinuser@example.com", + phone="+628123456789", + first_name="test", + last_name="user", + password=generate_hash_password("password"), + is_active=True, + ) db.add(user) else: user = existing_user - + if not existing_ticket: ticket = Ticket( - id=ticket_id, - name="Test Conference Ticket", - price=500000, - user_participant_type="In Person", - is_sold_out=False, - is_active=True, - description="Test ticket for payment", - ) + id=ticket_id, + name="Test Conference Ticket", + price=500000, + user_participant_type="In Person", + is_sold_out=False, + is_active=True, + description="Test ticket for payment", + ) db.add(ticket) else: ticket = existing_ticket - + if not existing_payment: payment = Payment( - id=payment_id, - user_id=user.id, - ticket_id=ticket.id, - payment_link="https://mayar.id/pay/test-link", - status=PaymentStatus.PAID, - created_at=get_current_time_in_timezone(), - mayar_id="mayar-test-id", - mayar_transaction_id="mayar-test-tx", - amount=500000, - description="Test payment", - ) + id=payment_id, + user_id=user.id, + ticket_id=ticket.id, + payment_link="https://mayar.id/pay/test-link", + status=PaymentStatus.PAID, + created_at=get_current_time_in_timezone(), + mayar_id="mayar-test-id", + mayar_transaction_id="mayar-test-tx", + amount=500000, + description="Test payment", + ) db.add(payment) else: payment = existing_payment - + db.commit() db.refresh(user) db.refresh(ticket) @@ -81,4 +87,4 @@ def initialize_checkin_data( except Exception as e: db.rollback() print(f"Error initializing checkin data: {e}") - raise \ No newline at end of file + raise From 1353964cb655b082d6a4a75eaa8ea819c914fe64 Mon Sep 17 00:00:00 2001 From: Nizar Izzuddin Yatim Fadlan Date: Tue, 9 Dec 2025 21:19:48 +0700 Subject: [PATCH 6/6] refactor: Enhance get_all_speakers function to support search functionality --- repository/speaker.py | 15 ++++++++++++++- routes/speaker.py | 4 +++- 2 files changed, 17 insertions(+), 2 deletions(-) diff --git a/repository/speaker.py b/repository/speaker.py index 6971181..9e47f6a 100644 --- a/repository/speaker.py +++ b/repository/speaker.py @@ -11,7 +11,11 @@ from schemas.speaker import SpeakerResponseItem -def get_all_speakers(db: Session, order_dir: Literal["asc", "desc"] = "asc"): +def get_all_speakers( + db: Session, + order_dir: Literal["asc", "desc"] = "asc", + search: Optional[str] = None, +): # Query dasar stmt = select(Speaker) @@ -21,6 +25,15 @@ def get_all_speakers(db: Session, order_dir: Literal["asc", "desc"] = "asc"): else: stmt = stmt.order_by(Speaker.updated_at.desc()) + if search: + search_pattern = f"%{search}%" + stmt = stmt.join(User, Speaker.user).where( + (User.username.ilike(search_pattern)) + | (User.first_name.ilike(search_pattern)) + | (User.last_name.ilike(search_pattern)) + | (User.email.ilike(search_pattern)) + ) + # Tambahkan pagination (offset + limit) # Hitung total data sebelum pagination total_count = db.scalar(select(func.count()).select_from(stmt.subquery())) diff --git a/routes/speaker.py b/routes/speaker.py index 08ba9c7..6d73126 100644 --- a/routes/speaker.py +++ b/routes/speaker.py @@ -60,7 +60,9 @@ async def get_speaker( ): try: if query.all: - data = speakerRepo.get_all_speakers(db=db, order_dir=query.order_dir) + data = speakerRepo.get_all_speakers( + db=db, search=query.search, order_dir=query.order_dir + ) else: data = speakerRepo.get_speaker_per_page_by_search( db=db,