From 24694ac30cd794f4ead81a0e803f37ed55127147 Mon Sep 17 00:00:00 2001 From: Kim Kakeya Date: Thu, 4 Dec 2025 02:08:55 -0300 Subject: [PATCH 1/2] feat: remove email from global ranking --- src/web/controllers/rank_controller.py | 8 +++--- src/web/mappers.py | 40 ++++++++++++++++++++++++-- src/web/schemas.py | 15 ++++++++-- tests/integration/test_ranking.py | 6 ++-- 4 files changed, 58 insertions(+), 11 deletions(-) diff --git a/src/web/controllers/rank_controller.py b/src/web/controllers/rank_controller.py index 9634179..0aad557 100644 --- a/src/web/controllers/rank_controller.py +++ b/src/web/controllers/rank_controller.py @@ -12,7 +12,7 @@ from ...core.models.user import User from ...core.services.score_service import ScoreService from ..auth import get_current_user -from ..mappers import map_user_domain_list_to_response +from ..mappers import map_user_domain_list_to_ranking_response from ..schemas import GlobalRankingResponse, UserRankingResponse router = APIRouter(prefix="/rank", tags=["ranking"]) @@ -64,9 +64,9 @@ async def get_global_ranking( score_service: Injected score service Returns: - List of all users ordered by score + List of all users ordered by score (names and scores only, no emails) """ users = await score_service.get_global_ranking() - user_responses = map_user_domain_list_to_response(users) + global_ranking_user_responses = map_user_domain_list_to_ranking_response(users) - return GlobalRankingResponse(users=user_responses) + return GlobalRankingResponse(users=global_ranking_user_responses) diff --git a/src/web/mappers.py b/src/web/mappers.py index 54526d6..993c9d6 100644 --- a/src/web/mappers.py +++ b/src/web/mappers.py @@ -18,6 +18,7 @@ BusRouteResponseSchema, CoordinateSchema, HistoryResponse, + RankingUserResponse, RouteIdentifierSchema, RouteShapeResponse, TripHistoryEntry, @@ -57,6 +58,37 @@ def map_user_domain_list_to_response(users: list[User]) -> list[UserResponse]: return [map_user_domain_to_response(user) for user in users] +def map_user_domain_to_ranking_response(user: User) -> RankingUserResponse: + """ + Map a User domain model to a RankingUserResponse schema (excludes email). + + Args: + user: User domain model + + Returns: + RankingUserResponse schema + """ + return RankingUserResponse( + name=user.name, + score=user.score, + ) + + +def map_user_domain_list_to_ranking_response( + users: list[User], +) -> list[RankingUserResponse]: + """ + Map a list of User domain models to RankingUserResponse schemas (excludes email). + + Args: + users: List of User domain models + + Returns: + List of RankingUserResponse schemas + """ + return [map_user_domain_to_ranking_response(user) for user in users] + + # ===== Route Mappers ===== @@ -223,7 +255,9 @@ def map_route_shape_to_response(shape: RouteShape) -> RouteShapeResponse: return RouteShapeResponse( route=map_route_identifier_domain_to_schema(shape.route), shape_id=shape.shape_id, - points=[map_coordinate_domain_to_schema(point.coordinate) for point in shape.points], + points=[ + map_coordinate_domain_to_schema(point.coordinate) for point in shape.points + ], ) @@ -270,4 +304,6 @@ def map_history_entries_to_response(entries: list[HistoryEntry]) -> HistoryRespo Returns: HistoryResponse for API """ - return HistoryResponse(trips=[map_history_entry_to_schema(entry) for entry in entries]) + return HistoryResponse( + trips=[map_history_entry_to_schema(entry) for entry in entries] + ) diff --git a/src/web/schemas.py b/src/web/schemas.py index e23ff6c..9bc43c1 100644 --- a/src/web/schemas.py +++ b/src/web/schemas.py @@ -159,7 +159,9 @@ class RouteShapeResponse(BaseModel): route: RouteIdentifierSchema = Field(..., description="Route identifier") shape_id: str = Field(..., description="GTFS shape identifier") - points: list[CoordinateSchema] = Field(..., description="Ordered list of coordinates") + points: list[CoordinateSchema] = Field( + ..., description="Ordered list of coordinates" + ) class RouteShapesRequest(BaseModel): @@ -179,6 +181,15 @@ class RouteShapesResponse(BaseModel): # ===== Ranking Schemas ===== +class RankingUserResponse(BaseModel): + """Response schema for user information in rankings (excludes email).""" + + name: str + score: int + + model_config = {"from_attributes": True} + + class UserRankingResponse(BaseModel): position: int = Field(..., description="User's rank position") @@ -186,7 +197,7 @@ class UserRankingResponse(BaseModel): class GlobalRankingResponse(BaseModel): """Response schema for global ranking.""" - users: list[UserResponse] = Field(..., description="List of users by rank") + users: list[RankingUserResponse] = Field(..., description="List of users by rank") # ===== History Schemas ===== diff --git a/tests/integration/test_ranking.py b/tests/integration/test_ranking.py index 5fde5d2..5280e81 100644 --- a/tests/integration/test_ranking.py +++ b/tests/integration/test_ranking.py @@ -109,9 +109,9 @@ async def test_get_global_ranking_should_work( first_user = data["users"][0] assert "name" in first_user - assert "email" in first_user + assert "email" not in first_user assert "score" in first_user - assert first_user["email"] == "first@example.com" + assert first_user["score"] == 1000 @pytest.mark.asyncio async def test_get_global_ranking_with_single_user( @@ -134,8 +134,8 @@ async def test_get_global_ranking_with_single_user( data = response.json() assert len(data["users"]) == 2 - assert data["users"][0]["email"] == "solo@example.com" assert data["users"][0]["score"] == 500 + assert "email" not in data["users"][0] @pytest.mark.asyncio async def test_get_global_ranking_without_auth_fails( From a3119cd94399eba9de889c9172481cf294396fb8 Mon Sep 17 00:00:00 2001 From: Kim Kakeya Date: Thu, 4 Dec 2025 10:14:03 -0300 Subject: [PATCH 2/2] style: ruff --- src/web/mappers.py | 8 ++------ src/web/schemas.py | 4 +--- 2 files changed, 3 insertions(+), 9 deletions(-) diff --git a/src/web/mappers.py b/src/web/mappers.py index 993c9d6..98f760d 100644 --- a/src/web/mappers.py +++ b/src/web/mappers.py @@ -255,9 +255,7 @@ def map_route_shape_to_response(shape: RouteShape) -> RouteShapeResponse: return RouteShapeResponse( route=map_route_identifier_domain_to_schema(shape.route), shape_id=shape.shape_id, - points=[ - map_coordinate_domain_to_schema(point.coordinate) for point in shape.points - ], + points=[map_coordinate_domain_to_schema(point.coordinate) for point in shape.points], ) @@ -304,6 +302,4 @@ def map_history_entries_to_response(entries: list[HistoryEntry]) -> HistoryRespo Returns: HistoryResponse for API """ - return HistoryResponse( - trips=[map_history_entry_to_schema(entry) for entry in entries] - ) + return HistoryResponse(trips=[map_history_entry_to_schema(entry) for entry in entries]) diff --git a/src/web/schemas.py b/src/web/schemas.py index 9bc43c1..04182ba 100644 --- a/src/web/schemas.py +++ b/src/web/schemas.py @@ -159,9 +159,7 @@ class RouteShapeResponse(BaseModel): route: RouteIdentifierSchema = Field(..., description="Route identifier") shape_id: str = Field(..., description="GTFS shape identifier") - points: list[CoordinateSchema] = Field( - ..., description="Ordered list of coordinates" - ) + points: list[CoordinateSchema] = Field(..., description="Ordered list of coordinates") class RouteShapesRequest(BaseModel):