From 36628b31e86cfa102e142cae63fb43306ca83c16 Mon Sep 17 00:00:00 2001 From: Andrew Shu Date: Sun, 31 Aug 2025 20:31:01 -0700 Subject: [PATCH] Refactor to clarify API does not use marshmallow * Get SimpleHoursPeriod directly, not via marshmallow schema Mentioning marshmallow in imports is misleading here, since we are constructing the result dict by hand and not using marshmallow to generate the OpenAPI response. * Rename API serialize methods to mention API This differentiates from the Marshmallow Schema dump method which is used in the JSON renderer in json.py --- tildes/tildes/views/api/beta/comment.py | 22 ++++++++++++++----- tildes/tildes/views/api/beta/topic.py | 29 +++++++++++++++---------- tildes/tildes/views/api/beta/user.py | 21 ++++++++++-------- 3 files changed, 45 insertions(+), 27 deletions(-) diff --git a/tildes/tildes/views/api/beta/comment.py b/tildes/tildes/views/api/beta/comment.py index e57b355..7d37aa2 100644 --- a/tildes/tildes/views/api/beta/comment.py +++ b/tildes/tildes/views/api/beta/comment.py @@ -8,8 +8,11 @@ from tildes.models.comment import Comment from tildes.models.comment.comment_tree import CommentInTree -def comment_to_dict(request: Request, comment: Comment) -> dict: - """Convert a Comment object to a dictionary for JSON serialization.""" +def comment_to_api_dict(request: Request, comment: Comment) -> dict: + """Convert a Comment object to a dictionary for JSON serialization. + + The schema is defined in our OpenAPI YAML file. + """ # Check permissions for viewing comment details (and set safe defaults) author = None @@ -66,14 +69,21 @@ def comment_to_dict(request: Request, comment: Comment) -> dict: } -def comment_subtree_to_dict(request: Request, comments: list[CommentInTree]) -> list: - """Convert a comment subtree to a list of dictionaries for JSON serialization.""" +def comment_subtree_to_api_dict( + request: Request, comments: list[CommentInTree] +) -> list: + """Convert a comment subtree to a list of dictionaries for JSON serialization. + + The schema is defined in our OpenAPI YAML file. + """ comments_list = [] for comment in comments: - comment_dict = comment_to_dict(request, comment) + comment_dict = comment_to_api_dict(request, comment) comment_dict["depth"] = comment.depth comment_dict["children"] = ( - comment_subtree_to_dict(request, comment.replies) if comment.replies else [] + comment_subtree_to_api_dict(request, comment.replies) + if comment.replies + else [] ) comments_list.append(comment_dict) return comments_list diff --git a/tildes/tildes/views/api/beta/topic.py b/tildes/tildes/views/api/beta/topic.py index 02a40f7..6b04bc8 100644 --- a/tildes/tildes/views/api/beta/topic.py +++ b/tildes/tildes/views/api/beta/topic.py @@ -3,24 +3,26 @@ """JSON API endpoints related to topics.""" -from marshmallow.exceptions import ValidationError from pyramid.request import Request from pyramid.view import view_config from tildes.enums import CommentTreeSortOption, TopicSortOption from tildes.models.comment import CommentTree, Comment from tildes.models.topic import Topic -from tildes.schemas.fields import ShortTimePeriod +from tildes.lib.datetime import SimpleHoursPeriod from tildes.lib.id import id36_to_id from tildes.views.api.beta.api_utils import ( build_error_response, get_next_and_prev_link, query_apply_pagination, ) -from tildes.views.api.beta.comment import comment_subtree_to_dict +from tildes.views.api.beta.comment import comment_subtree_to_api_dict -def topic_to_dict(topic: Topic) -> dict: - """Convert a Topic object to a dictionary for JSON serialization.""" +def topic_to_api_dict(topic: Topic) -> dict: + """Convert a Topic object to a dictionary for JSON serialization. + + The schema is defined in our OpenAPI YAML file. + """ return { "id": topic.topic_id36, "title": topic.title, @@ -59,10 +61,13 @@ def get_topics(request: Request) -> dict: # noqa after = request.openapi_validated.parameters.query.get("after", None) # Parse parameters where necessary - try: - period = ShortTimePeriod(allow_none=True).deserialize(period_raw) - except ValidationError as exc: - return build_error_response(str(exc), field="period") + if not period_raw or period_raw == "all": + period = None + else: + try: + period = SimpleHoursPeriod.from_short_form(period_raw) + except ValueError as exc: + return build_error_response(str(exc), field="period") try: if order_raw: @@ -94,7 +99,7 @@ def get_topics(request: Request) -> dict: # noqa # Build the JSON topic data for topic in topics: - processed_topic = topic_to_dict(topic) + processed_topic = topic_to_api_dict(topic) processed_topics.append(processed_topic) # Construct the paging next and previous link if there are more topics @@ -161,11 +166,11 @@ def get_topic(request: Request) -> dict: tree.uncollapse_new_comments(topic.last_visit_time) tree.finalize_collapsing_maximized() - commentsjson = comment_subtree_to_dict(request, tree.tree) + commentsjson = comment_subtree_to_api_dict(request, tree.tree) # Construct the final response JSON object response = { - "topic": topic_to_dict(topic), + "topic": topic_to_api_dict(topic), "comments": commentsjson, } return response diff --git a/tildes/tildes/views/api/beta/user.py b/tildes/tildes/views/api/beta/user.py index 3b5b865..0f97ea1 100644 --- a/tildes/tildes/views/api/beta/user.py +++ b/tildes/tildes/views/api/beta/user.py @@ -15,12 +15,15 @@ from tildes.views.api.beta.api_utils import ( get_next_and_prev_link, query_apply_pagination, ) -from tildes.views.api.beta.comment import comment_to_dict -from tildes.views.api.beta.topic import topic_to_dict +from tildes.views.api.beta.comment import comment_to_api_dict +from tildes.views.api.beta.topic import topic_to_api_dict -def _user_to_dict(user: User) -> dict: - """Convert a User object to a dictionary for JSON serialization.""" +def _user_to_api_dict(user: User) -> dict: + """Convert a User object to a dictionary for JSON serialization. + + The schema is defined in our OpenAPI YAML file. + """ return { "username": user.username, "joined_at": user.created_time.isoformat(), @@ -85,16 +88,16 @@ def get_user(request: Request) -> dict: # noqa processed_results = [] for item in combined_results.results: if isinstance(item, Topic): - processed_results.append(topic_to_dict(item)) + processed_results.append(topic_to_api_dict(item)) elif isinstance(item, Comment): - processed_results.append(comment_to_dict(request, item)) + processed_results.append(comment_to_api_dict(request, item)) # Construct the paging next and previous link if there are more topics (next_link, prev_link) = get_next_and_prev_link(request, combined_results) # Construct the final response JSON object response = { - "user": _user_to_dict(user), + "user": _user_to_api_dict(user), "history": processed_results, "pagination": { "num_items": len(processed_results), @@ -144,7 +147,7 @@ def get_user_comments(request: Request) -> dict: # Build the JSON history data processed_comments = [] for comment in query_result.results: - processed_comments.append(comment_to_dict(request, comment)) + processed_comments.append(comment_to_api_dict(request, comment)) # Construct the paging next and previous link if there are more comments (next_link, prev_link) = get_next_and_prev_link(request, query_result) @@ -200,7 +203,7 @@ def get_user_topics(request: Request) -> dict: # Build the JSON history data processed_topics = [] for topic in query_result.results: - processed_topics.append(topic_to_dict(topic)) + processed_topics.append(topic_to_api_dict(topic)) # Construct the paging next and previous link if there are more topics (next_link, prev_link) = get_next_and_prev_link(request, query_result)