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