Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
53 changes: 53 additions & 0 deletions djangoplicity/blog/api/v2/serializers.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,53 @@
from rest_framework import serializers
from djangoplicity.archives.api.v2.serializers import ArchiveSerializerMixin
from djangoplicity.metadata.api.v2.serializers import ProgramSerializer
from djangoplicity.media.api.v2.serializers import ImageMiniSerializer
from djangoplicity.blog.models import Post

class PostMiniSerializer(ArchiveSerializerMixin, serializers.ModelSerializer):
banner_image = serializers.SerializerMethodField()
programs = ProgramSerializer(many=True)

class Meta:
model = Post
fields = [
'slug',
'lang',
'url',
'title',
'subtitle',
'release_date',
'programs',
'banner_image',
]

def get_banner_image(self, obj):
if obj.banner:
return ImageMiniSerializer(obj.banner).data
return None


class PostSerializer(ArchiveSerializerMixin, serializers.ModelSerializer):
banner_image = serializers.SerializerMethodField()
programs = ProgramSerializer(many=True)

class Meta:
model = Post
fields = [
'slug',
'lang',
'url',
'title',
'subtitle',
'lede',
'body',
'links',
'release_date',
'programs',
'banner_image',
]

def get_banner_image(self, obj):
if obj.banner:
return ImageMiniSerializer(obj.banner).data
return None
11 changes: 11 additions & 0 deletions djangoplicity/blog/api/v2/urls.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,11 @@
from django.urls import path, include
from rest_framework import routers
from djangoplicity.blog.api.v2.views import PostListView, PostDetailView

api_router = routers.DefaultRouter()
api_router.register('', PostListView)
api_router.register('', PostDetailView)

urlpatterns = [
path('', include(api_router.urls))
]
96 changes: 96 additions & 0 deletions djangoplicity/blog/api/v2/views.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,96 @@
from djangoplicity.blog.models import Post
from djangoplicity.blog.options import BlogPostOptions
from djangoplicity.translation.api.v2.views import TranslationAPIViewMixin, DEFAULT_API_TRANSLATION_MODE
from drf_spectacular.types import OpenApiTypes
from drf_spectacular.utils import extend_schema, OpenApiParameter
from rest_framework.pagination import PageNumberPagination
from django.db.models import Q

from djangoplicity.blog.api.v2.serializers import PostMiniSerializer, PostSerializer
from rest_framework import permissions, mixins
from rest_framework.viewsets import GenericViewSet
from django_filters import rest_framework as filters


class PostPagination(PageNumberPagination):
page_size = 10
page_size_query_param = 'page_size'
max_page_size = 50


class PostFilter(filters.FilterSet):
program = filters.CharFilter(field_name="programs__url")
search = filters.CharFilter(method='search_filter')
is_e_and_e = filters.BooleanFilter(field_name="is_e_and_e")

class Meta:
model = Post
fields = ['program', 'is_e_and_e']

def search_filter(self, queryset, name, value):
if value:
return queryset.filter(
Q(title__icontains=value) |
Q(subtitle__icontains=value) |
Q(lede__icontains=value) |
Q(body__icontains=value)
)
return queryset


class PostViewMixin:
def get_queryset(self):
is_e_and_e = self.request.GET.get('is_e_and_e', 'false').lower()

if is_e_and_e == 'true':
qs, _ = BlogPostOptions.Queries.e_and_e.queryset(
Post, BlogPostOptions, self.request,
mode=self.request.GET.get('translation_mode', DEFAULT_API_TRANSLATION_MODE)
)
else:
qs, _ = BlogPostOptions.Queries.default.queryset(
Post, BlogPostOptions, self.request,
mode=self.request.GET.get('translation_mode', DEFAULT_API_TRANSLATION_MODE)
)

return qs


@extend_schema(
parameters=[
OpenApiParameter(
"program",
OpenApiTypes.STR,
description="The program identifier, e.g: kpno, rubin, gemini, ctio, csdc, noao, useltp, noirlab"
),
OpenApiParameter(
"search",
OpenApiTypes.STR,
description="Search by title, subtitle, lede, or body"
),
OpenApiParameter(
"is_e_and_e",
OpenApiTypes.BOOL,
description="If you select ‘true’, you will receive the E&E category posts.",
default=False
),
OpenApiParameter(
"page_size",
OpenApiTypes.INT,
description=f"Number of results to return per page. Max: {PostPagination.max_page_size}, Default: {PostPagination.page_size}"
),
],
)
class PostListView(mixins.ListModelMixin, PostViewMixin, TranslationAPIViewMixin, GenericViewSet):
permission_classes = [permissions.AllowAny]
queryset = Post.objects.none()
serializer_class = PostMiniSerializer
pagination_class = PostPagination
filter_backends = (filters.DjangoFilterBackend,)
filterset_class = PostFilter


class PostDetailView(mixins.RetrieveModelMixin, PostViewMixin, TranslationAPIViewMixin, GenericViewSet):
permission_classes = [permissions.AllowAny]
queryset = Post.objects.none()
serializer_class = PostSerializer
42 changes: 41 additions & 1 deletion djangoplicity/blog/options.py
Original file line number Diff line number Diff line change
Expand Up @@ -31,7 +31,7 @@

from django.utils.translation import ugettext_noop as _

from djangoplicity.archives.contrib.browsers import ListBrowser
from djangoplicity.archives.contrib.browsers import ListBrowser, SerializationBrowser
from djangoplicity.archives.contrib.queries.defaults import AllPublicQuery, \
EmbargoQuery
from djangoplicity.archives.options import ArchiveOptions
Expand All @@ -41,6 +41,16 @@
from djangoplicity.blog.views import PostDetailView



from djangoplicity.archives.contrib.queries.defaults import AdvancedSearchQuery
from djangoplicity.archives.contrib.search.fields import DateSinceSearchField, DateUntilSearchField, IdSearchField, TextSearchField
from djangoplicity.archives.contrib.serialization import JSONEmitter
from djangoplicity.archives.views import SerializationDetailView
from .models import Post
from .queries import PostsAllPublicQuery, PostsEAndEQuery, PostsProgramsQuery
from .serializers import PostSerializer


class PostOptions(ArchiveOptions):
slug_field = 'slug'
urlname_prefix = 'blog'
Expand Down Expand Up @@ -74,3 +84,33 @@ def feeds():
return {
'': PostFeed,
}


class BlogPostOptions(ArchiveOptions):
urlname_prefix = 'blogs'

search_fields = ('slug', 'title', 'lede', 'body')

class Queries:
default = PostsAllPublicQuery(browsers=('normal', 'json'), verbose_name=_(("Blog Posts")), feed_name="default")
e_and_e = PostsEAndEQuery(browsers=('normal', 'json'), verbose_name=_(("E&E Blog Posts")))
program = PostsProgramsQuery(relation_field='programs', browsers=('normal', 'json'), verbose_name=_(("Blog Posts by Program")))
search = AdvancedSearchQuery(browsers=('normal', 'json'), verbose_name=_(("Advanced Blog Search")), searchable=False)

class Browsers:
normal = ListBrowser(verbose_name=_(('View all')), paginate_by=100)
json = SerializationBrowser(serializer=PostSerializer, emitter=JSONEmitter, paginate_by=20, display=False, verbose_name=_(("JSON")))

detail_views = (
{ 'url_pattern': 'api/(?P<serializer>json)/', 'view': SerializationDetailView(serializer=PostSerializer, emitters=[JSONEmitter]), 'urlname_suffix': 'serialization' },
)

class AdvancedSearch:
slug = TextSearchField(label=_(('Post Slug')), model_field='slug')
published_since = DateSinceSearchField(label=_(("Published since")), model_field='release_date')
published_until = DateUntilSearchField(label=_(("Published until")), model_field='release_date')
title = TextSearchField(label=_(("Title")), model_field='title')
body = TextSearchField(label=_(("Body")), model_field='body')

class Meta:
verbose_name = _(("Advanced Blog Search"))
33 changes: 33 additions & 0 deletions djangoplicity/blog/queries.py
Original file line number Diff line number Diff line change
Expand Up @@ -36,6 +36,9 @@

from datetime import datetime

from djangoplicity.archives.contrib.queries import AllPublicQuery
from djangoplicity.metadata.archives.queries import ProgramPublicQuery


class PostTagQuery(CategoryQuery):
'''
Expand All @@ -46,3 +49,33 @@ def queryset( self, model, options, request, stringparam=None, **kwargs ):
qs, categories = super(PostTagQuery, self).queryset(model, options, request, stringparam)

return (qs.filter(Q(release_date__lte=datetime.now()) | Q(release_date__isnull=True)), categories)


class PostsAllPublicQuery(AllPublicQuery):
'''
Query to hide is_e_and_e posts
'''
def queryset(self, model, options, request, **kwargs):
(qs, query_data) = super(PostsAllPublicQuery, self).queryset(model, options, request, **kwargs)
qs = qs.filter(is_e_and_e=False)
return (qs, query_data)


class PostsEAndEQuery(AllPublicQuery):
'''
Query to show only is_e_and_e posts
'''
def queryset(self, model, options, request, **kwargs):
(qs, query_data) = super(PostsEAndEQuery, self).queryset(model, options, request, **kwargs)
qs = qs.filter(is_e_and_e=True)
return (qs, query_data)


class PostsProgramsQuery(ProgramPublicQuery):
'''
Query to filter posts by program, excluding is_e_and_e posts
'''
def queryset(self, model, options, request, **kwargs):
(qs, query_data) = super(PostsProgramsQuery, self).queryset(model, options, request, **kwargs)
qs = qs.filter(is_e_and_e=False)
return (qs, query_data)
52 changes: 52 additions & 0 deletions djangoplicity/blog/serializers.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,52 @@
from django.contrib.sites.shortcuts import get_current_site
from djangoplicity.archives.contrib.serialization import SimpleSerializer
from djangoplicity.archives.utils import get_instance_archives, get_instance_archives_urls
from djangoplicity.blog.models import Post

# ==========================================
# Blog Posts
# ==========================================
class PostSerializer(SimpleSerializer):
fields = (
'slug',
'title',
'subtitle',
'lede',
'body',
'links',
'release_date',
'visuals',
'lang',
'url',
)

def get_visuals_value(self, obj):
if obj.banner:
return {
'id': obj.banner.id,
'width': obj.banner.width,
'height': obj.banner.height,
'formats': get_instance_archives(obj.banner),
'formats_url': get_instance_archives_urls(obj.banner),
'url': f'http://{get_current_site(None)}{obj.banner.get_absolute_url()}',
'title': obj.banner.title,
}
return None

def get_url_value(self, obj):
return f'http://{get_current_site(None)}{obj.get_absolute_url()}'


class MiniPostSerializer(SimpleSerializer):
fields = (
'slug',
'title',
'subtitle',
'lede',
'release_date',
'lang',
'main_visual',
)

def get_main_visual_value(self, obj):
return obj.banner.id if obj.banner else None