mirror of https://gitlab.com/tildes/tildes.git
4 changed files with 147 additions and 2 deletions
-
1tildes/tildes/models/topic/topic_query.py
-
6tildes/tildes/routes.py
-
1tildes/tildes/views/api/beta/__init__.py
-
141tildes/tildes/views/api/beta/topic.py
@ -0,0 +1 @@ |
|||
"""Contains views for the JSON web API""" |
@ -0,0 +1,141 @@ |
|||
# Copyright (c) 2018 Tildes contributors <code@tildes.net> |
|||
# SPDX-License-Identifier: AGPL-3.0-or-later |
|||
|
|||
"""JSON API endpoints related to topics.""" |
|||
|
|||
from marshmallow.exceptions import ValidationError |
|||
from pyramid.exceptions import HTTPBadRequest |
|||
from pyramid.request import Request |
|||
from pyramid.response import Response |
|||
from pyramid.view import view_config |
|||
from tildes.enums import TopicSortOption |
|||
from tildes.models.topic import Topic |
|||
from tildes.schemas.fields import ShortTimePeriod |
|||
|
|||
|
|||
@view_config(route_name="apibeta.topics", openapi=True, renderer="json") |
|||
def get_topics(request: Request) -> dict: |
|||
"""Get topics""" |
|||
limit = request.openapi_validated.parameters.query.get("limit") |
|||
period_raw = request.openapi_validated.parameters.query.get("period") |
|||
tag = request.openapi_validated.parameters.query.get("tag", None) |
|||
order_raw = request.openapi_validated.parameters.query.get("order", None) |
|||
before = request.openapi_validated.parameters.query.get("before", None) |
|||
after = request.openapi_validated.parameters.query.get("after", None) |
|||
|
|||
# Parse parameters where necessary |
|||
try: |
|||
period = ShortTimePeriod(allow_none=True) |
|||
period = period.deserialize(period_raw) |
|||
except ValidationError as exc: |
|||
return Response( |
|||
status=400, |
|||
content_type="application/json", |
|||
json=[ |
|||
{"message": str(exc), "field": "period", "exception": "ValidationError"} |
|||
], |
|||
) |
|||
|
|||
try: |
|||
if order_raw: |
|||
order = TopicSortOption[order_raw.upper()] |
|||
else: |
|||
order = TopicSortOption.ACTIVITY |
|||
except KeyError: |
|||
return Response( |
|||
status=400, |
|||
content_type="application/json", |
|||
json=[ |
|||
{ |
|||
"message": f"Invalid order value: {order_raw}", |
|||
"field": "order", |
|||
"exception": "ValidationError", |
|||
} |
|||
], |
|||
) |
|||
|
|||
query = request.query(Topic).join_all_relationships().apply_sort_option(order) |
|||
|
|||
# restrict the time period, if not set to "all time" |
|||
if period: |
|||
query = query.inside_time_period(period) |
|||
|
|||
# restrict to a specific tag if provided |
|||
if tag: |
|||
query = query.has_tag(tag) |
|||
|
|||
# apply before/after pagination restrictions if relevant |
|||
try: |
|||
if before and after: |
|||
raise ValueError("Cannot specify both before and after parameters") |
|||
if before: |
|||
query = query.before_id36(before) |
|||
if after: |
|||
query = query.after_id36(after) |
|||
except ValueError as exc: |
|||
return Response( |
|||
status=400, |
|||
content_type="application/json", |
|||
json=[ |
|||
{ |
|||
"message": str(exc), |
|||
"field": "before/after", |
|||
"exception": "ValidationError", |
|||
} |
|||
], |
|||
) |
|||
|
|||
# Execute the query |
|||
topics = query.get_page(limit) |
|||
processed_topics = [] |
|||
|
|||
# Build the JSON topic data |
|||
for topic in topics: |
|||
processed_topic = { |
|||
"id": topic.topic_id, |
|||
"title": topic.title, |
|||
"text_html": topic.rendered_html, |
|||
"url": topic.link, |
|||
"comments_url": topic.permalink, |
|||
"group": str(topic.group.path), |
|||
"content_metadata": topic.content_metadata_for_display, |
|||
"created_at": topic.created_time.isoformat(), |
|||
"posted_by_user": topic.user.username, |
|||
"vote_count": topic.num_votes, |
|||
"comment_count": topic.num_comments, |
|||
"new_comment_count": topic.comments_since_last_visit, |
|||
"voted": topic.user_voted, |
|||
"bookmarked": topic.user_bookmarked, |
|||
"ignored": topic.user_ignored, |
|||
"official": topic.is_official, |
|||
"tags": topic.tags, |
|||
} |
|||
processed_topics.append(processed_topic) |
|||
|
|||
# Construct the paging next and previous link if there are more topics |
|||
if topics.has_next_page: |
|||
query_vars = request.GET.copy() |
|||
query_vars.pop("before", None) |
|||
query_vars.update({"after": topics.next_page_after_id36}) |
|||
next_link = request.current_route_url(_query=query_vars) |
|||
else: |
|||
next_link = None |
|||
|
|||
if topics.has_prev_page: |
|||
query_vars = request.GET.copy() |
|||
query_vars.pop("after", None) |
|||
query_vars.update({"before": topics.prev_page_before_id36}) |
|||
prev_link = request.current_route_url(_query=query_vars) |
|||
else: |
|||
prev_link = None |
|||
|
|||
# Construct the final response JSON object |
|||
response = { |
|||
"topics": processed_topics, |
|||
"pagination": { |
|||
"num_items": len(processed_topics), |
|||
"next_link": next_link, |
|||
"prev_link": prev_link, |
|||
}, |
|||
} |
|||
return response |
Write
Preview
Loading…
Cancel
Save
Reference in new issue