podjango: Add "Podcast" model to support multiple podcasts

Each Cast (episode) can belong to one or more Podcast, allowing episodes to be
shared between podcasts. This enables us introductory episodes to be delivered
in their own feed, but also included in the main "The Corresponding Source"
feed.

This required adding an additional `podcast_slug` argument to most views. The
date archive views were dropped because they're not linked to from anywhere.

Added a `podcasts` view as an index of all available Podcasts.
This commit is contained in:
Ben Sturmfels 2024-04-25 15:32:24 +10:00
parent 9447528938
commit 956f8c6fda
Signed by: bsturmfels
GPG key ID: 023C05E2C9C068F0
23 changed files with 277 additions and 202 deletions

View file

@ -18,7 +18,12 @@
#
from django.contrib import admin
from .models import Cast, CastTag
from .models import Cast, CastTag, Podcast
@admin.register(Podcast)
class PodcastAdmin(admin.ModelAdmin):
prepopulated_fields = {'slug': ('title',)}
@admin.register(CastTag)
@ -26,12 +31,10 @@ class CastTagAdmin(admin.ModelAdmin):
prepopulated_fields = {'slug': ('label',)}
@admin.register(Cast)
class CastAdmin(admin.ModelAdmin):
list_display = ('pub_date', 'title')
list_filter = ['pub_date']
list_filter = ['pub_date', 'podcast']
date_hierarchy = 'pub_date'
search_fields = ['title', 'summary', 'body']
prepopulated_fields = {'slug': ("title",)}

View file

@ -21,10 +21,12 @@ from datetime import datetime
from django.contrib.sites.shortcuts import get_current_site
from django.contrib.syndication.views import Feed, add_domain
from django.shortcuts import render
from django.shortcuts import get_object_or_404, render
from django.urls import reverse
from django.utils.feedgenerator import Rss201rev2Feed
from .models import Cast
from .models import Cast, Podcast
from .templatetags.podjango import episode_url
# FIXME: Settings here should not be hard-coded for given casts, but
# should instead have settings from the main screen.
@ -103,7 +105,7 @@ def podcast_helper_add_root_elements(self, handler):
# handler.addQuickElement('dc:creator', self.feed['dcCreator'])
handler.addQuickElement('itunes:explicit', self.feed['iTunesExplicit'])
handler.addQuickElement('itunes:block', self.feed['iTunesBlock'])
handler.addQuickElement('generator', 'http://www.faif.us/code')
handler.addQuickElement('generator', 'https://sfconservancy.org/')
handler.addQuickElement('media:thumbnail', '' , { 'url' : self.feed['rssImage']['url'] })
handler.addQuickElement('itunes:image', '' , { 'href' : self.feed['iTunesImage']['url']})
@ -188,7 +190,7 @@ class CastFeed(CastFeedBase):
link = "/cast/"
description = "A bi-weekly discussion of legal, policy, and other issues in the open source and software freedom community (including occasional interviews) from Brooklyn, New York, USA. Presented by Karen Sandler and Bradley M. Kuhn."
author_email = "podcast@faif.us"
author_link = "http://www.faif.us/"
author_link = "https://sfconservancy.org/"
author_name = "Software Freedom Conservancy"
title_template = "feeds/podcast_title.html"
description_template = "feeds/podcast_description.html"
@ -202,14 +204,18 @@ class CastFeed(CastFeedBase):
self.is_secure = request.is_secure()
return super().get_feed(obj, request)
def items(self):
return Cast.objects.filter(pub_date__lte=datetime.now()).order_by('-pub_date')
def get_object(self, request, podcast_slug):
self.podcast = Podcast.objects.get(slug=podcast_slug)
return self.podcast
def items(self, obj):
return Cast.objects.filter(podcast=obj, pub_date__lte=datetime.now()).order_by('-pub_date')
def item_pubdate(self, item):
return item.pub_date
def item_link(self, item):
return item.get_absolute_url()
return episode_url(self.podcast, item)
def item_author_email(self, obj):
return "oggcast@faif.us"
@ -218,7 +224,7 @@ class CastFeed(CastFeedBase):
return "Software Freedom Conservancy"
def item_author_link(self, obj):
return "http://faif.us"
return "https://sfconservancy.org"
def item_categories(self, item):
return ('Technology',)
@ -264,19 +270,17 @@ class OggCastFeed(CastFeed):
return item.ogg_length
feed_dict = {
'cast-ogg': OggCastFeed,
'cast-mp3': Mp3CastFeed,
}
# make each feed know its canonical url
for k, v in feed_dict.items():
v.get_absolute_url = '/feeds/%s/' % k
def view(request):
def view(request, podcast_slug):
"""Listing of all available feeds
"""
feed_dict = {
'feed-ogg': OggCastFeed,
'feed-mp3': Mp3CastFeed,
}
podcast = get_object_or_404(Podcast, slug=podcast_slug)
# make each feed know its canonical url
for k, v in feed_dict.items():
v.get_absolute_url = reverse(f'podjango:{k}', kwargs={'podcast_slug': podcast.slug})
feeds = feed_dict.values()
return render(request, "feeds.html", {'feeds': feeds})
return render(request, "feeds.html", {'podcast': podcast,
'feeds': feeds})

View file

@ -19,19 +19,19 @@
from datetime import datetime
from django.shortcuts import render
from django.shortcuts import get_object_or_404, render
from .models import Cast
from .models import Cast, Podcast
def view(request):
def view(request, podcast_slug):
"""Cast front page view
Performs all object queries necessary to render the front page.
"""
cast = Cast.objects.all().filter(pub_date__lte=datetime.now())[:3]
podcast = get_object_or_404(Podcast, slug=podcast_slug)
cast = Cast.objects.filter(podcast=podcast, pub_date__lte=datetime.now())[:3]
c = {
'cast': cast,
'podcast': podcast,
}
return render(request, "podjango/frontpage.html", c)

View file

@ -0,0 +1,33 @@
# Generated by Django 4.2.11 on 2024-04-24 04:03
from django.db import migrations, models
class Migration(migrations.Migration):
dependencies = [
('podjango', '0002_alter_cast_tags'),
]
operations = [
migrations.CreateModel(
name='Podcast',
fields=[
(
'id',
models.AutoField(
auto_created=True,
primary_key=True,
serialize=False,
verbose_name='ID',
),
),
('title', models.CharField(max_length=50)),
],
),
migrations.AddField(
model_name='cast',
name='podcast',
field=models.ManyToManyField(to='podjango.podcast'),
),
]

View file

@ -0,0 +1,35 @@
# Generated by Django 4.2.11 on 2024-04-24 04:10
from django.db import migrations, models
class Migration(migrations.Migration):
dependencies = [
('podjango', '0003_podcast_cast_podcast'),
]
operations = [
migrations.AlterModelOptions(
name='cast',
options={
'get_latest_by': 'pub_date',
'ordering': ('-pub_date',),
'verbose_name': 'episode',
'verbose_name_plural': 'episodes',
},
),
migrations.AlterModelOptions(
name='casttag',
options={
'verbose_name': 'episode tag',
'verbose_name_plural': 'episode tags',
},
),
migrations.AddField(
model_name='podcast',
name='slug',
field=models.SlugField(default='', unique=True),
preserve_default=False,
),
]

View file

@ -0,0 +1,18 @@
# Generated by Django 4.2.11 on 2024-04-25 01:21
from django.db import migrations, models
class Migration(migrations.Migration):
dependencies = [
('podjango', '0004_alter_cast_options_alter_casttag_options_and_more'),
]
operations = [
migrations.AddField(
model_name='podcast',
name='long_description',
field=models.TextField(blank=True),
),
]

View file

@ -22,6 +22,16 @@ from django.db import models
from django.urls import reverse
class Podcast(models.Model):
"""An ongoing series of episodes."""
title = models.CharField(max_length=50)
slug = models.SlugField(unique=True)
long_description = models.TextField(blank=True)
def __str__(self):
return self.title
class CastTag(models.Model):
"""Tagging for casts"""
@ -30,13 +40,13 @@ class CastTag(models.Model):
class Meta:
db_table = 'cast_tags' # legacy
verbose_name = 'episode tag'
verbose_name_plural = 'episode tags'
def __str__(self):
return self.label
def get_absolute_url(self):
return reverse('podjango:cast') + "?tag=%s" % self.slug
class CastManager(models.Manager):
def get_queryset(self):
@ -45,13 +55,14 @@ class CastManager(models.Manager):
class Cast(models.Model):
"""Cast"""
"""A podcast episode."""
title = models.CharField(max_length=200)
slug = models.SlugField(unique=True)
summary = models.TextField(help_text="Use raw HTML. This summary is not included at the beginning of the body when the entry is displayed. It used only for the material on the front page.")
body = models.TextField(help_text="Use raw HTML. Include the full body of any show notes or other information about this episode. It will be labelled on the site as Show Notes. It is included on the detail entry, and in the description data on the cast RSS feed.")
pub_date = models.DateTimeField()
podcast = models.ManyToManyField(Podcast)
tags = models.ManyToManyField(CastTag, blank=True)
ogg_path = models.CharField(
max_length=300, blank=True,
@ -69,23 +80,16 @@ class Cast(models.Model):
class Meta:
db_table = 'casts_entries' # legacy
verbose_name_plural = 'casts'
verbose_name = 'episode'
verbose_name_plural = 'episodes'
ordering = ('-pub_date',)
get_latest_by = 'pub_date'
def __str__(self):
return self.title
def get_absolute_url(self):
return reverse(
'podjango:detail',
kwargs={
'year': self.pub_date.year,
'month': self.pub_date.strftime("%b").lower(),
'day': self.pub_date.day,
'slug': self.slug,
}
)
def month_str(self):
return self.pub_date.strftime("%b").lower()
def is_recent(self):
return self.pub_date > (datetime.now() - timedelta(days=14))

View file

@ -17,19 +17,4 @@
}
a.feedlink img { margin-left: 0.5rem }
</style>
<link rel="alternate" type="application/rss+xml" title="MP3 Audio Feed" href="{% url 'podjango:feed-mp3' %}" />
<link rel="alternate" type="application/rss+xml" title="Ogg/Vorbis Audio Feed" href="{% url 'podjango:feed-ogg' %}" />
{% endblock %}
{% comment %}
{% block internal_navigate %}
<h3>Tags</h3>
<ul>
{% for tag in all_tags %}
<li><a href="{{ tag.get_absolute_url }}">{{ tag.label }}</a></li>
{% endfor %}
</ul>
<p><a href="{% url 'podjango:cast' %}">All oggcasts&hellip;</a></p>
{% endblock %}
{% endcomment %}

View file

@ -1,27 +0,0 @@
<!-- FIXME: SFLC specific content -->
<!-- Copyright (C) 2008 Bradley M. Kuhn <bkuhn@ebb.org> -->
<!-- Permission is granted to copy, modify, redistribute, propagate, -->
<!-- and/or convey this template in any form. -->
{% extends "podjango/base_podcast.html" %}
{% block subtitle %}Archive: {{ day|date:"F j, Y" }} - {% endblock %}
{% block content %}
<h1>{% include 'podjango/feed_links.inc.html' %} <a href="{% url 'podjango:cast-home' %}">The Corresponding Source</h1>
<h2>Archive: {{ day|date:"F j, Y" }}</h2>
{% for object in object_list %}
<div class="pa2 mb2" style="background: #F0FFB8">
<h3><a href="{{ object.get_absolute_url }}">{{ object.title|safe }}</a></h3>
<p class="date">{{ object.pub_date|date:"F j, Y" }}</p>
{{ object.summary|safe }}
<p><span class="continued"><a href="{{ object.get_absolute_url
}}">Read More...</a></span></p>
<p>Released on {{ object.pub_date|date:"F j, Y" }}; its running time is {{ object.duration }}</p>
{% if object.tags.all %}<p class="blog-tags small">Tags: {% for tag in object.tags.all %}<a href="{{ tag.get_absolute_url }}">{{ tag.label }}</a>{% if not forloop.last %}, {% endif %}{% endfor %}</p>{% endif %}
</div>
{% endfor %}
{% endblock %}

View file

@ -1,27 +0,0 @@
<!-- FIXME: SFLC specific content -->
<!-- Copyright (C) 2008 Bradley M. Kuhn <bkuhn@ebb.org> -->
<!-- Permission is granted to copy, modify, redistribute, propagate, -->
<!-- and/or convey this template in any form. -->
{% extends "podjango/base_podcast.html" %}
{% block subtitle %}Archive: {{ month|date:"F, Y" }} - {% endblock %}
{% block content %}
<h1>{% include 'podjango/feed_links.inc.html' %} <a href="{% url 'podjango:cast-home' %}">The Corresponding Source</h1>
<h2>Archive: {{ month|date:"F, Y" }}</h2>
{% for object in object_list %}
<div class="pa2 mb2" style="background: #F0FFB8">
<h3><a href="{{ object.get_absolute_url }}">{{ object.title|safe }}</a></h3>
<p class="date">{{ object.pub_date|date:"F j, Y" }}</p>
{{ object.summary|safe }}
<p><span class="continued"><a href="{{ object.get_absolute_url
}}">Read More...</a></span></p>
<p>Released on {{ object.pub_date|date:"F j, Y" }}; its running time is {{ object.duration }}</p>
{% if object.tags.all %}<p class="blog-tags small">Tags: {% for tag in object.tags.all %}<a href="{{ tag.get_absolute_url }}">{{ tag.label }}</a>{% if not forloop.last %}, {% endif %}{% endfor %}</p>{% endif %}
</div>
{% endfor %}
{% endblock %}

View file

@ -1,22 +0,0 @@
<!-- FIXME: SFLC specific content -->
<!-- Copyright (C) 2008 Bradley M. Kuhn <bkuhn@ebb.org> -->
<!-- Permission is granted to copy, modify, redistribute, propagate, -->
<!-- and/or convey this template in any form. -->
{% extends "podjango/base_podcast.html" %}
{% block subtitle %}Archive: {{ year }} - {% endblock %}
{% block content %}
<h1>{% include 'podjango/feed_links.inc.html' %} <a href="{% url 'podjango:cast-home' %}">The Corresponding Source</h1>
<h2>Archive: {{ year }}</h2>
<ul>
{% for object in object_list %}
<li><a href="{{ object.get_absolute_url }}"><b>{{ object.title|safe }}</b></a><br/>
<i>{{ object.pub_date|date:"F j, Y" }}; duration: {{ object.duration }}</i></li>
{% endfor %}
</ul>
{% endblock %}

View file

@ -4,11 +4,13 @@
<!-- and/or convey this template in any form. -->
{% extends "podjango/base_podcast.html" %}
{% block subtitle %}{{ object.title|striptags }} - The Corresponding Source - {% endblock %}
{% load podjango %}
{% block subtitle %}{{ object.title|striptags }} - {{ podcast.title }} - {% endblock %}
{% block content %}
<h1>{% include 'podjango/feed_links.inc.html' %} <a href="{% url 'podjango:cast-home' %}">The Corresponding Source</h1>
<h1>{% include 'podjango/feed_links.inc.html' %} <a href="{% url 'podjango:cast-home' podcast_slug=podcast.slug %}">{{ podcast.title }}</h1>
<h2><a class="feedlink" href="{{ object.ogg_path }}">{% include 'podjango/audio_ogg_button.inc.html' %}</a>
<a class="feedlink" href="{{ object.mp3_path }}">{% include 'podjango/audio_mp3_button.inc.html' %}</a>
@ -41,8 +43,8 @@ running time is {{ object.duration}}.</p>
{% include "podjango/license.inc.html" %}
</div>
{% if object.tags.all %}<p class="blog-tags">Tags: {% for tag in object.tags.all %}<a href="{{ tag.get_absolute_url }}">{{ tag.label }}</a>{% if not forloop.last %}, {% endif %}{% endfor %}</p>{% endif %}
{% if object.tags.all %}<p class="blog-tags">Tags: {% for tag in object.tags.all %}<a href="{% tag_url podcast tag %}">{{ tag.label }}</a>{% if not forloop.last %}, {% endif %}{% endfor %}</p>{% endif %}
<p><span class="continued"><a href="{% url 'podjango:cast' %}">Other episodes&hellip;</a></span></p>
<p><span class="continued"><a href="{% url 'podjango:cast' podcast_slug=podcast.slug %}">Other episodes&hellip;</a></span></p>
{% endblock %}

View file

@ -4,15 +4,23 @@
<!-- and/or convey this template in any form. -->
{% extends "podjango/base_podcast.html" %}
{% block subtitle %}The Corresponding Source - {% endblock %}
{% load podjango %}
{% block header %}
{{ block.super }}
<link rel="alternate" type="application/rss+xml" title="MP3 Audio Feed" href="{% url 'podjango:feed-mp3' podcast_slug=podcast.slug %}" />
<link rel="alternate" type="application/rss+xml" title="Ogg/Vorbis Audio Feed" href="{% url 'podjango:feed-ogg' podcast_slug=podcast.slug %}" />
{% endblock %}
{% block subtitle %}{{ podcast.title }} - {% endblock %}
{% block content %}
<h1>{% include 'podjango/feed_links.inc.html' %} <a href="{% url 'podjango:cast-home' %}">The Corresponding Source</h1>
<h1>{% include 'podjango/feed_links.inc.html' %} <a href="{% url 'podjango:cast-home' podcast_slug=podcast.slug %}">{{ podcast.title }}</a></h1>
{% if tags %}
<p>Displaying casts
tagged {% for tag in tags %}{% if not forloop.last %}{% if not forloop.first %}, {% endif %}{% endif %}<a href="{{ tag.get_absolute_url }}">{{ tag.label }}</a>{% if forloop.revcounter == 2 %} or {% endif %}{% endfor %}
tagged: {% for tag in tags %}{% if not forloop.last %}{% if not forloop.first %}, {% endif %}{% endif %}<a href="{% tag_url podcast tag %}">{{ tag.label }}</a>{% if forloop.revcounter == 2 %} or {% endif %}{% endfor %}
</p>
{% endif %}
@ -22,7 +30,7 @@ tagged {% for tag in tags %}{% if not forloop.last %}{% if not forloop.first %},
<a class="feedlink" href="{{ object.ogg_path }}">{% include 'podjango/audio_ogg_button.inc.html' %}</a>
<a class="feedlink" href="{{ object.mp3_path }}">{% include 'podjango/audio_mp3_button.inc.html' %}</a>
<a href="{{ object.get_absolute_url }}">{{ object.title|safe }}</a></h3>
<a href="{% episode_url podcast object %}">{{ object.title|safe }}</a></h3>
<p class="date">{{ object.pub_date|date:"F j, Y" }}</p>
<h4>Summary</h4>
@ -36,7 +44,7 @@ tagged {% for tag in tags %}{% if not forloop.last %}{% if not forloop.first %},
{{ object.body|safe }}
</div>
{% if object.tags.all %}<p class="cast-tags small">Tags: {% for tag in object.tags.all %}<a href="{{ tag.get_absolute_url }}">{{ tag.label }}</a>{% if not forloop.last %}, {% endif %}{% endfor %}</p>{% endif %}
{% if object.tags.all %}<p class="cast-tags small">Tags: {% for tag in object.tags.all %}<a href="{% tag_url podcast tag %}">{{ tag.label }}</a>{% if not forloop.last %}, {% endif %}{% endfor %}</p>{% endif %}
</div>
{% endfor %}

View file

@ -1,8 +1,8 @@
{% load static %}
<a href="{% url 'podjango:feed-ogg' %}" class="feedlink">
<a href="{% url 'podjango:feed-ogg' podcast_slug=podcast.slug %}" class="feedlink">
<img src="{% static 'podjango/img/cast/rss-audioogg.png' %}" alt="[Ogg/Vorbis Audio RSS]"/>
</a>
<a href="{% url 'podjango:feed-mp3' %}" class="feedlink">
<a href="{% url 'podjango:feed-mp3' podcast_slug=podcast.slug %}" class="feedlink">
<img src="{% static 'podjango/img/cast/rss-audiomp3.png' %}" alt="[MP3 Audio RSS]"/>
</a>

View file

@ -1,37 +1,35 @@
{% extends "podjango/base_podcast.html" %}
{% block content %}
{% block header %}
{{ block.super }}
<link rel="alternate" type="application/rss+xml" title="MP3 Audio Feed" href="{% url 'podjango:feed-mp3' podcast_slug=podcast.slug %}" />
<link rel="alternate" type="application/rss+xml" title="Ogg/Vorbis Audio Feed" href="{% url 'podjango:feed-ogg' podcast_slug=podcast.slug %}" />
{% endblock %}
{% block content %}
<div class="singleColumn">
<h1>The Corresponding Source</h1>
<h1>{{ podcast.title }}</h1>
<p>The Corresponding Source (formerly Free as in Freedom) is a bi-weekly oggcast, hosted and presented by
<a href="http://ebb.org/bkuhn">Bradley M. Kuhn</a> and <a href="http://gnomg.org">Karen Sandler</a>.
The discussion includes legal, policy, and many other issues in the Free, Libre,
and Open Source Software (FLOSS) world. Occasionally, guests join
Bradley and Karen to discuss various topics regarding FLOSS.</p>
{% include "podjango/credits.inc.html" %}
{% include "podjango/feedback.inc.html" %}
{{ podcast.long_description|safe }}
<h2>Follow the RSS and Other Feeds</h2>
<p>There is RSS for both <a href="{% url 'podjango:feed-ogg' %}">ogg format</a>
and <a href="{% url 'podjango:feed-mp3' %}">mp3 format</a>.<!-- These links <em>might</em>
work if you want to <a href="itpc://faif.us{% url 'podjango:feed-mp3' %}">subscribe to the show</a> <a href="https://itunes.apple.com/us/podcast/free-as-in-freedom/id450458894">with proprietary Apple devices.</a>--></p>
<p>There is RSS for both <a href="{% url 'podjango:feed-ogg' podcast_slug=podcast.slug %}">ogg format</a>
and <a href="{% url 'podjango:feed-mp3' podcast_slug=podcast.slug %}">mp3 format</a>.<!-- These links <em>might</em>
work if you want to <a href="itpc://faif.us{% url 'podjango:feed-mp3' podcast_slug=podcast.slug %}">subscribe to the show</a> <a href="https://itunes.apple.com/us/podcast/free-as-in-freedom/id450458894">with proprietary Apple devices.</a>--></p>
<h2>{% include 'podjango/feed_links.inc.html' %}<a href="{% url 'podjango:cast' %}">Recent Shows</a></h2>
<h2>{% include 'podjango/feed_links.inc.html' %}<a href="{% url 'podjango:cast' podcast_slug=podcast.slug %}">Recent Shows</a></h2>
{% for cc in cast %}
<div class="pa2 mb2" style="background: #F0FFB8">
<a class="feedlink" href="{{ cc.ogg_path }}">{% include 'podjango/audio_ogg_button.inc.html' %}</a>
<a class="feedlink" href="{{ cc.mp3_path }}">{% include 'podjango/audio_mp3_button.inc.html' %}</a>
<h3><a href="{{ cc.get_absolute_url }}">{{ cc.title|safe }}</a></h3>
<h3><a href="{% url 'podjango:detail' podcast_slug=podcast.slug year=cc.pub_date.year month=cc.month_str day=cc.pub_date.day slug=cc.slug %}">{{ cc.title|safe }}</a></h3>
<p class="date">{{ cc.pub_date|date:"F j, Y" }}</p>
{{ cc.summary|safe }}
</div>
{% endfor %}
<p><a href="{% url 'podjango:cast' %}">All oggcasts&hellip;</a></p>
<p><a href="{% url 'podjango:cast' podcast_slug=podcast.slug %}">All oggcasts&hellip;</a></p>
</div>
{% endblock %}

View file

@ -0,0 +1,18 @@
<!-- FIXME: SFLC specific content -->
<!-- Copyright (C) 2008 Bradley M. Kuhn <bkuhn@ebb.org> -->
<!-- Permission is granted to copy, modify, redistribute, propagate, -->
<!-- and/or convey this template in any form. -->
{% extends "podjango/base_podcast.html" %}
{% load podjango %}
{% block subtitle %}Casts - {% endblock %}
{% block content %}
<h1>Casts</h1>
{% for podcast in podcasts %}
<p><a href="{% url 'podjango:cast-home' podcast_slug=podcast.slug %}">{{ podcast.title }}</a></p>
{% endfor %}
{% endblock %}

View file

@ -0,0 +1,26 @@
from django import template
from django.urls import reverse
register = template.Library()
@register.simple_tag
def tag_url(podcast, tag):
return '{base_url}?tag={tag_slug}'.format(
base_url=reverse('podjango:cast', kwargs={'podcast_slug': podcast.slug}),
tag_slug=tag.slug
)
@register.simple_tag
def episode_url(podcast, cast):
return reverse(
'podjango:detail',
kwargs={
'podcast_slug': podcast.slug,
'year': cast.pub_date.year,
'month': cast.pub_date.strftime('%b').lower(),
'day': cast.pub_date.day,
'slug': cast.slug,
}
)

View file

@ -20,39 +20,54 @@
import datetime
from django.conf import settings
from django.shortcuts import get_object_or_404
from django.urls import path
from django.views.generic.dates import (
DateDetailView,
DayArchiveView,
MonthArchiveView,
YearArchiveView,
)
from django.views.generic.dates import DateDetailView
from . import frontpage
from .feeds import Mp3CastFeed, OggCastFeed, view
from .models import Cast, CastTag
from .views import custom_index, query
from .models import Cast, CastTag, Podcast
from . import views
app_name = 'podjango'
extra_context = {}
info_dict = {
'queryset': Cast.objects.all(),
'date_field': 'pub_date',
'extra_context': extra_context,
'template_name': 'podjango/cast/cast_detail.html',
}
class PodcastDateDetailView(DateDetailView):
date_field = 'pub_date'
model = Cast
def get(self, request, podcast_slug, *args, **kwargs):
self.podcast = get_object_or_404(Podcast, slug=podcast_slug)
return super().get(request, *args, **kwargs)
def get_queryset(self):
return super().get_queryset().filter(podcast=self.podcast)
def get_context_data(self, **kwargs):
context = super().get_context_data(**kwargs)
context['podcast'] = self.podcast
return context
urlpatterns = [
path('', frontpage.view, name='cast-home'),
path('<int:year>/<month>/<int:day>/<slug:slug>/', DateDetailView.as_view(**info_dict), name='detail'),
path('<int:year>/<month>/<int:day>/', DayArchiveView.as_view(**info_dict), name='day-archive'),
path('<int:year>/<month>/', MonthArchiveView.as_view(**info_dict), name='month-archive'),
path('<int:year>/', YearArchiveView.as_view(**info_dict), name='year-archive'),
path('all/', custom_index, dict(info_dict, paginate_by=20), name='cast'),
path('feeds/ogg/', OggCastFeed(), name='feed-ogg'),
path('feeds/mp3/', Mp3CastFeed(), name='feed-mp3'),
path('feeds/', view, name='feeds'),
path('', views.podcasts, name='podcasts'),
path('<slug:podcast_slug>/', frontpage.view, name='cast-home'),
path(
'<slug:podcast_slug>/<int:year>/<month>/<int:day>/<slug:slug>/',
PodcastDateDetailView.as_view(
template_name='podjango/cast_detail.html',
),
name='detail'
),
path('<slug:podcast_slug>/all/', views.custom_index, info_dict, name='cast'),
path('<slug:podcast_slug>/feeds/ogg/', OggCastFeed(), name='feed-ogg'),
path('<slug:podcast_slug>/feeds/mp3/', Mp3CastFeed(), name='feed-mp3'),
path('<slug:podcast_slug>/feeds/', view, name='feeds'),
]
if settings.DEBUG:

View file

@ -22,7 +22,12 @@ from operator import or_
from django.shortcuts import get_object_or_404, render
from .models import CastTag
from .models import Cast, CastTag, Podcast
def podcasts(request):
podcasts = Podcast.objects.all()
return render(request, 'podjango/podcasts.html', {'podcasts': podcasts})
def OR_filter(field_name, objs):
@ -35,11 +40,12 @@ def last_name(person):
return person.formal_name.rpartition(' ')[2]
def custom_index(request, queryset, *args, **kwargs):
def custom_index(request, podcast_slug, *args, **kwargs):
"""Cast list view that allows scrolling and also shows an index by
year.
"""
podcast = get_object_or_404(Podcast, slug=podcast_slug)
kwargs = kwargs.copy()
kwargs['extra_context'] = kwargs.get('extra_context', {}).copy()
extra_context = kwargs['extra_context']
@ -48,30 +54,26 @@ def custom_index(request, queryset, *args, **kwargs):
del kwargs['date_field']
if not kwargs.get('allow_future', False):
queryset = queryset.filter(**{'%s__lte' % date_field: datetime.now()})
authors = []
if 'author' in request.GET:
authors = [get_object_or_404(Person, username=author)
for author in request.GET.getlist('author')]
extra_context['authors'] = authors
queryset = queryset.filter(OR_filter('author', authors))
queryset = Cast.objects.filter(
podcast=podcast,
**{'%s__lte' % date_field: datetime.now()},
)
tags = []
if 'tag' in request.GET:
tags = [get_object_or_404(CastTag, slug=tag)
for tag in request.GET.getlist('tag')]
extra_context['tags'] = tags
queryset = queryset.filter(OR_filter('tags', tags))
queryset = queryset.filter(OR_filter('tags', tags), podcast=podcast)
if authors or tags:
query_string = '&'.join(['author=%s' % a.username for a in authors]
+ ['tag=%s' % t.slug for t in tags])
if tags:
query_string = '&'.join('tag=%s' % t.slug for t in tags)
extra_context['query_string'] = query_string
else:
date_list = queryset.dates(date_field, 'year')
extra_context['date_list'] = date_list
# TODO
return render(request, 'podjango/cast/cast_list.html', {'object_list': queryset})
return render(request, 'podjango/cast_list.html', {'podcast': podcast,
'object_list': queryset,
'tags': tags})

View file

@ -1,5 +1,5 @@
<ul>
<li><a href="{% url 'podjango:cast-home' %}">The Corresponding Source</a></li>
<li><a href="{% url 'podjango:cast-home' podcast_slug='the-corresponding-source' %}">The Corresponding Source</a></li>
<li><a href="/copyleft-compliance/glossary.html">Glossary of Terms</a></li>
<li><a href="/copyleft-compliance/vizio.html">Vizio Lawsuit</a></li>
<li><a href="/press/qanda.html">Q&amp;A</a></li>

View file

@ -1,5 +1,5 @@
<ul>
<li><a href="{% url 'podjango:cast-home' %}">The Corresponding Source</a></li>
<li><a href="{% url 'podjango:cast-home' podcast_slug='the-corresponding-source' %}">The Corresponding Source</a></li>
<li class="CopyleftCompliance"><a href="/copyleft-compliance/">Copyleft Compliance</a></li>
<li class="VizioTopBar"><a href="/copyleft-compliance/vizio.html">Vizio Lawsuit</a></li>
<li class="FIXME"><a href="/projects/">Member Projects</a></li>

View file

@ -29,7 +29,7 @@ urlpatterns = [
path('admin/', admin.site.urls),
path('assignment/', include('conservancy.assignment.urls')),
path('blog/', include('conservancy.blog.urls')),
path('casts/the-corresponding-source/', include('conservancy.podjango.urls')),
path('casts/', include('conservancy.podjango.urls')),
path('contacts/', include('conservancy.contacts.urls')),
path('contractpatch/', include('conservancy.contractpatch.urls')),
path('feeds/', feeds.view),