navigation & breadcrumbs

This commit is contained in:
bkfox
2023-12-12 20:07:58 +01:00
parent eaa1e2412a
commit 46a9008cda
29 changed files with 454 additions and 347 deletions

View File

@ -1,7 +1,7 @@
from . import admin, errors
from .article import ArticleDetailView, ArticleListView
from .base import BaseAPIView, BaseView
from .diffusion import DiffusionListView
from .diffusion import DiffusionListView, TimeTableView
from .episode import EpisodeDetailView, EpisodeListView, PodcastListView
from .home import HomeView
from .log import LogListAPIView, LogListView
@ -26,6 +26,7 @@ __all__ = (
"BaseAPIView",
"BaseView",
"DiffusionListView",
"TimeTableView",
"EpisodeDetailView",
"EpisodeListView",
"PodcastListView",
@ -40,4 +41,15 @@ __all__ = (
"ProgramListView",
"ProgramPageDetailView",
"ProgramPageListView",
"attached",
)
attached = {}
for key in __all__:
view = globals().get(key)
if key == "attached":
continue
if attach := getattr(view, "attach_to_value", None):
attached[attach] = view

View File

@ -16,4 +16,4 @@ class ArticleListView(PageListView):
model = Article
has_headline = True
parent_model = Program
attach_to_value = StaticPage.ATTACH_TO_ARTICLES
attach_to_value = StaticPage.Target.ARTICLES

View File

@ -18,6 +18,26 @@ class BaseView(TemplateResponseMixin, ContextMixin):
# def get_queryset(self):
# return super().get_queryset().station(self.station)
def get_nav_menu(self):
menu = []
for item in self.station.navitem_set.all():
try:
if item.page:
view = item.page.get_related_view()
secondary = view and view.get_secondary_nav()
else:
secondary = None
menu.append((item, secondary))
except:
import traceback
traceback.print_exc()
raise
return menu
def get_secondary_nav(self):
return None
def get_related_queryset(self):
"""Return a queryset of related pages or None."""
return None
@ -46,6 +66,8 @@ class BaseView(TemplateResponseMixin, ContextMixin):
kwargs["title"] = page.display_title
kwargs["cover"] = page.cover and page.cover.url
if "nav_menu" not in kwargs:
kwargs["nav_menu"] = self.get_nav_menu()
return super().get_context_data(**kwargs)
def dispatch(self, *args, **kwargs):

View File

@ -1,29 +1,54 @@
import datetime
from django.urls import reverse
from django.views.generic import ListView
from aircox.models import Diffusion, StaticPage
from aircox.models import Diffusion, Log, StaticPage
from .base import BaseView
from .mixins import AttachedToMixin, GetDateMixin
__all__ = ("DiffusionListView",)
__all__ = ("DiffusionListView", "TimeTableView")
class DiffusionListView(GetDateMixin, AttachedToMixin, BaseView, ListView):
class BaseDiffusionListView(AttachedToMixin, BaseView, ListView):
model = Diffusion
queryset = Diffusion.objects.on_air().order_by("-start")
class DiffusionListView(BaseDiffusionListView):
"""View for timetables."""
model = Diffusion
redirect_date_url = "diffusion-list"
attach_to_value = StaticPage.ATTACH_TO_DIFFUSIONS
class TimeTableView(GetDateMixin, BaseDiffusionListView):
model = Diffusion
redirect_date_url = "timetable-list"
attach_to_value = StaticPage.Target.TIMETABLE
template_name = "aircox/timetable_list.html"
def get_date(self):
date = super().get_date()
return date if date is not None else datetime.date.today()
def get_queryset(self):
return super().get_queryset().date(self.date).order_by("start")
def get_logs(self, date):
return Log.objects.on_air().date(self.date).filter(track__isnull=False)
def get_context_data(self, **kwargs):
def get_queryset(self):
return super().get_queryset().date(self.date)
@classmethod
def get_secondary_nav(cls):
date = datetime.date.today()
start = date - datetime.timedelta(days=date.weekday())
dates = [start + datetime.timedelta(days=i) for i in range(0, 7)]
return tuple((date.strftime("%A %d"), reverse("timetable-list", kwargs={"date": date})) for date in dates)
def get_context_data(self, object_list=None, **kwargs):
start = self.date - datetime.timedelta(days=self.date.weekday())
dates = [start + datetime.timedelta(days=i) for i in range(0, 7)]
return super().get_context_data(date=self.date, dates=dates, **kwargs)
if object_list is None:
logs = self.get_logs(self.date)
object_list = Log.merge_diffusions(logs, self.object_list)
return super().get_context_data(date=self.date, dates=dates, object_list=object_list, **kwargs)

View File

@ -33,9 +33,9 @@ class EpisodeListView(PageListView):
model = Episode
filterset_class = EpisodeFilters
parent_model = Program
attach_to_value = StaticPage.ATTACH_TO_EPISODES
attach_to_value = StaticPage.Target.EPISODES
class PodcastListView(EpisodeListView):
attach_to_value = StaticPage.ATTACH_TO_PODCASTS
attach_to_value = StaticPage.Target.PODCASTS
queryset = Episode.objects.published().with_podcasts().order_by("-pub_date")

View File

@ -10,7 +10,7 @@ from .mixins import AttachedToMixin
class HomeView(AttachedToMixin, BaseView, ListView):
template_name = "aircox/home.html"
attach_to_value = StaticPage.ATTACH_TO_HOME
attach_to_value = StaticPage.Target.HOME
model = Diffusion
queryset = Diffusion.objects.on_air().select_related("episode").order_by("-start")

View File

@ -6,12 +6,12 @@ from django.views.decorators.cache import cache_page
from django.views.generic import ListView
from rest_framework.generics import ListAPIView
from ..models import Diffusion, Log, StaticPage
from ..models import Diffusion, Log
from ..serializers import LogInfo, LogInfoSerializer
from .base import BaseAPIView, BaseView
from .mixins import AttachedToMixin, GetDateMixin
__all__ = ["LogListMixin", "LogListView"]
__all__ = ("LogListMixin", "LogListView", "LogListAPIView")
class LogListMixin(GetDateMixin):
@ -62,7 +62,6 @@ class LogListView(AttachedToMixin, BaseView, LogListMixin, ListView):
`request.GET`, defaults to today)."""
redirect_date_url = "log-list"
attach_to_value = StaticPage.ATTACH_TO_LOGS
def get_date(self):
date = super().get_date()

View File

@ -44,16 +44,16 @@ class ParentMixin:
parent = None
"""Parent page object."""
def get_parent(self, request, *args, **kwargs):
def get_parent(self, request, **kwargs):
if self.parent_model is None or self.parent_url_kwarg not in kwargs:
return
lookup = {self.parent_field: kwargs[self.parent_url_kwarg]}
return get_object_or_404(self.parent_model.objects.select_related("cover"), **lookup)
def get(self, request, *args, **kwargs):
self.parent = self.get_parent(request, *args, **kwargs)
return super().get(request, *args, **kwargs)
def dispatch(self, request, *args, **kwargs):
self.parent = self.get_parent(request, **kwargs)
return super().dispatch(request, *args, **kwargs)
def get_queryset(self):
if self.parent is not None:
@ -61,9 +61,10 @@ class ParentMixin:
return super().get_queryset()
def get_context_data(self, **kwargs):
self.parent = kwargs.setdefault("parent", self.parent)
if self.parent is not None:
kwargs.setdefault("cover", self.parent.cover.url)
parent = kwargs.setdefault("parent", self.parent)
if parent is not None:
kwargs.setdefault("cover", parent.cover.url)
return super().get_context_data(**kwargs)

View File

@ -1,11 +1,12 @@
from django.http import Http404, HttpResponse
from django.utils.translation import gettext_lazy as _
from django.views.generic import DetailView, ListView
from django.urls import reverse
from honeypot.decorators import check_honeypot
from ..filters import PageFilters
from ..forms import CommentForm
from ..models import Comment
from ..models import Comment, Category
from ..utils import Redirect
from .base import BaseView
from .mixins import AttachedToMixin, FiltersMixin, ParentMixin
@ -18,7 +19,25 @@ __all__ = [
]
class BasePageListView(AttachedToMixin, ParentMixin, BaseView, ListView):
class BasePageMixin:
category = None
def get_category(self, page, **kwargs):
if page:
if getattr(page, "category_id", None):
return page.category
if page.parent_id:
return self.get_category(page.parent_subclass)
if slug := self.kwargs.get("category_slug"):
return Category.objects.get(slug=slug)
return None
def get_context_data(self, *args, **kwargs):
kwargs.setdefault("category", self.category)
return super().get_context_data(*args, **kwargs)
class BasePageListView(AttachedToMixin, BasePageMixin, ParentMixin, BaseView, ListView):
"""Base view class for BasePage list."""
template_name = "aircox/basepage_list.html"
@ -26,11 +45,18 @@ class BasePageListView(AttachedToMixin, ParentMixin, BaseView, ListView):
paginate_by = 30
has_headline = True
def dispatch(self, request, *args, **kwargs):
return super().dispatch(request, *args, **kwargs)
def get(self, *args, **kwargs):
self.category = self.get_category(self.parent)
return super().get(*args, **kwargs)
def get_queryset(self):
return super().get_queryset().select_subclasses().published().select_related("cover")
query = super().get_queryset().select_subclasses().published().select_related("cover")
if self.category:
query = query.filter(category=self.category)
return query
def get_context_data(self, **kwargs):
kwargs.setdefault("has_headline", self.has_headline)
@ -48,12 +74,15 @@ class BasePageListView(AttachedToMixin, ParentMixin, BaseView, ListView):
return context
class BasePageDetailView(BaseView, DetailView):
class BasePageDetailView(BasePageMixin, BaseView, DetailView):
"""Base view class for BasePage."""
template_name = "aircox/basepage_detail.html"
context_object_name = "page"
def get(self, *args, **kwargs):
return super().get(*args, **kwargs)
def get_context_data(self, **kwargs):
if self.object.cover:
kwargs.setdefault("cover", self.object.cover.url)
@ -81,6 +110,8 @@ class BasePageDetailView(BaseView, DetailView):
if redirect_url:
raise Redirect(redirect_url)
raise Http404("%s not found" % self.model._meta.verbose_name)
self.category = self.get_category(obj)
return obj
def get_page(self):
@ -105,26 +136,22 @@ class PageListView(FiltersMixin, BasePageListView):
def get_queryset(self):
qs = super().get_queryset().select_related("category").order_by("-pub_date")
cat_ids = self.model.objects.published().values_list("category_id", flat=True)
self.categories = Category.objects.filter(id__in=cat_ids)
return qs
@classmethod
def get_secondary_nav(cls):
cat_ids = cls.model.objects.published().values_list("category_id", flat=True)
categories = Category.objects.filter(id__in=cat_ids)
return tuple(
(category.title, reverse(cls.model.list_url_name, kwargs={"category_slug": category.slug}))
for category in categories
)
def get_context_data(self, **kwargs):
self.categories = {
id: title
for title, id in self.model.objects.published()
.filter(category__isnull=False)
.values_list("category__title", "category__id")
.distinct()
}
cat_id = self.request.GET.get("category__id")
if cat_id:
cat_id = int(cat_id)
kwargs["category_id"] = cat_id
context = super().get_context_data(**kwargs)
if context.get("parent") and not cat_id:
kwargs["category_id"] = context["parent"].category_id
return context
kwargs["categories"] = self.categories
return super().get_context_data(**kwargs)
class PageDetailView(BasePageDetailView):
@ -152,7 +179,6 @@ class PageDetailView(BasePageDetailView):
if related:
related = related[: self.related_count]
kwargs["related_objects"] = related
kwargs["related_url"] = self.get_related_url()
return super().get_context_data(**kwargs)
@classmethod

View File

@ -52,7 +52,7 @@ class ProgramDetailView(BaseProgramMixin, PageDetailView):
class ProgramListView(PageListView):
model = Program
attach_to_value = StaticPage.ATTACH_TO_PROGRAMS
attach_to_value = StaticPage.Target.PROGRAMS
# FIXME: not used