diff --git a/website/api/filters.py b/website/api/filters.py new file mode 100644 index 0000000..e31476c --- /dev/null +++ b/website/api/filters.py @@ -0,0 +1,22 @@ +from typing import Any + +from django.db.models import Value +from django.http.request import HttpRequest +from rest_framework.filters import SearchFilter +from wagtail.query import PageQuerySet +from wagtail.search.utils import parse_query_string + + +class WagtailSearchFilter(SearchFilter): + def filter_queryset( + self, request: HttpRequest, queryset: PageQuerySet, view: Any + ) -> PageQuerySet: + search_query = request.query_params.get(self.search_param, "") + + if not search_query: + return queryset.annotate(relevance=Value(0)).none() + + filters, query = parse_query_string(search_query) + results = queryset.search(query).annotate_score("relevance") + + return results.get_queryset() diff --git a/website/api/pagination.py b/website/api/pagination.py new file mode 100644 index 0000000..e41bae8 --- /dev/null +++ b/website/api/pagination.py @@ -0,0 +1,6 @@ +from rest_framework.pagination import PageNumberPagination + + +class CustomPageNumberPagination(PageNumberPagination): + page_size = 20 + max_page_size = 50 diff --git a/website/api/serializers.py b/website/api/serializers.py index bd11c08..611088e 100644 --- a/website/api/serializers.py +++ b/website/api/serializers.py @@ -1,6 +1,8 @@ from rest_framework import serializers from wagtail.models import Page +from website.blog.models import BlogPostListPage + class PageLinkSerializer(serializers.ModelSerializer): full_url = serializers.SerializerMethodField() @@ -11,3 +13,14 @@ class PageLinkSerializer(serializers.ModelSerializer): def get_full_url(self, page: Page) -> str: return page.get_full_url(request=self.context["request"]) + + +class LMOTFYSerializer(serializers.ModelSerializer): + full_url = serializers.SerializerMethodField() + + class Meta: + model = BlogPostListPage + fields = read_only_fields = ["full_url", "title", "summary"] + + def get_full_url(self, page: Page) -> str: + return page.get_full_url(request=self.context["request"]) diff --git a/website/api/urls.py b/website/api/urls.py index ab356b8..cb5d2c7 100644 --- a/website/api/urls.py +++ b/website/api/urls.py @@ -11,6 +11,7 @@ app_name = "api" api_urlpatterns = [ path("ping", views.PingAPIView.as_view(), name="ping"), path("page-links", views.PageLinksAPIView.as_view(), name="page-links"), + path("lmotfy", views.LMOTFYAPIView.as_view(), name="lmotfy"), ] schema_view = get_schema_view( diff --git a/website/api/views.py b/website/api/views.py index 9362904..97f5936 100644 --- a/website/api/views.py +++ b/website/api/views.py @@ -1,11 +1,15 @@ from django.http.request import HttpRequest +from rest_framework.filters import OrderingFilter from rest_framework.generics import ListAPIView from rest_framework.response import Response from rest_framework.views import APIView from wagtail.models import Page from wagtail.query import PageQuerySet -from . import serializers +from website.blog.models import BlogPostListPage + +from . import filters, serializers +from .pagination import CustomPageNumberPagination class PingAPIView(APIView): @@ -27,3 +31,18 @@ class PageLinksAPIView(ListAPIView): .only("id", "url_path", "title") .order_by("title") ) + + +class LMOTFYAPIView(ListAPIView): + """ + Let Me Orange That For You + """ + + filter_backends = [filters.WagtailSearchFilter, OrderingFilter] + serializer_class = serializers.LMOTFYSerializer + ordering_fields = ["title", "date", "relevance"] + ordering = ["relevance"] + pagination_class = CustomPageNumberPagination + + def get_queryset(self) -> PageQuerySet: + return BlogPostListPage.objects.live().only("id", "url_path", "title", "body")