Skip to content
This repository was archived by the owner on Jan 7, 2026. It is now read-only.
Draft
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
43 commits
Select commit Hold shift + click to select a range
39498fb
refactor: merge into single app
danjac Jan 4, 2026
f22fb01
tests: test tidyup
danjac Jan 6, 2026
d4a2711
feat: add categories
danjac Jan 6, 2026
12a23c7
refactor: split up middleware
danjac Jan 6, 2026
e1c8971
build: add Hetzner terraform config
danjac Jan 6, 2026
46d1f88
chore: auto-update dependencies
danjac Jan 6, 2026
711358c
fix: category migration fix
danjac Jan 6, 2026
315a202
fix: admin episode detail link
danjac Jan 6, 2026
bf25318
refactor: use search mixin
danjac Jan 6, 2026
ad6ab32
test: more search tests
danjac Jan 6, 2026
aeec548
refactor: tidy up migrations
danjac Jan 6, 2026
26bfd80
build: tidy up justfile commands
danjac Jan 6, 2026
4645965
Rename test_sanitizier.py to test_sanitizer.py to fix typo
Copilot Jan 6, 2026
c8863c2
Move cover image tags to templatetags/covers.py and update templates
Copilot Jan 6, 2026
b5355e8
Fix type annotations and consolidate load statements per review feedback
Copilot Jan 6, 2026
1a50526
Fix template formatting per precommit (djade) requirements
Copilot Jan 6, 2026
894dedb
Refactor templatetags into separate modules and update templates
Copilot Jan 7, 2026
3dc59a7
Add page_layout library loads to all templates using title_tag
Copilot Jan 7, 2026
6936cc9
Fix missing page_layout loads in episode and podcast detail templates
Copilot Jan 7, 2026
ab083f6
fix: precommit fixes
danjac Jan 7, 2026
6248431
fix: import issue with absolute_uri
danjac Jan 7, 2026
ac579c4
Move title_tag and meta_tags to head_tags.py and rename page_layout.p…
Copilot Jan 7, 2026
afc3b6d
Rename templatetags/audio_player.py to templatetags/player.py (#362)
Copilot Jan 7, 2026
c027449
Consolidate template tags into single module with builtins registrati…
Copilot Jan 7, 2026
9059cfa
Consolidate search views into episodes and podcasts modules (#366)
Copilot Jan 7, 2026
805ffeb
Consolidate middleware package into single module (#368)
Copilot Jan 7, 2026
705ce28
Move search_people view to podcasts namespace (#370)
Copilot Jan 7, 2026
104ffcc
Consolidate history and bookmarks into episodes module (#372)
Copilot Jan 7, 2026
68cfd4e
Initial plan (#374)
Copilot Jan 7, 2026
dbeffef
Initial plan (#376)
Copilot Jan 7, 2026
a1552d7
[WIP] Tidy up view tests by merging into test episodes (#379)
Copilot Jan 7, 2026
d21222b
refactor: move search templates (#381)
danjac Jan 7, 2026
8e3e9db
Merge player module into episodes module (#380)
Copilot Jan 7, 2026
9b72ba0
Merge subscriptions module into podcasts (#383)
Copilot Jan 7, 2026
22d26f3
refactor: rename detail to episode_detail
danjac Jan 7, 2026
3dc4b38
refactor: tidy up template tags
danjac Jan 7, 2026
eac3810
Consolidate private feeds into podcasts module (#385)
Copilot Jan 7, 2026
d436201
Consolidate categories into podcasts module (#387)
Copilot Jan 7, 2026
4cce2cc
refactor: update user urls
danjac Jan 7, 2026
a8e0cee
Initial plan
Copilot Jan 7, 2026
e121a1d
Reorganize episodes and podcasts views into separate modules
Copilot Jan 7, 2026
47fe666
Split test files for episodes and podcasts views
Copilot Jan 7, 2026
0920b01
fix: test fixes
danjac Jan 7, 2026
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
The table of contents is too big for display.
Diff view
Diff view
  •  
  •  
  •  
8 changes: 3 additions & 5 deletions config/settings.py
Original file line number Diff line number Diff line change
Expand Up @@ -52,9 +52,7 @@
"health_check.contrib.redis",
"heroicons",
"widget_tweaks",
"simplecasts.episodes",
"simplecasts.podcasts",
"simplecasts.users",
"simplecasts",
]


Expand All @@ -77,8 +75,8 @@
"simplecasts.middleware.HtmxCacheMiddleware",
"simplecasts.middleware.HtmxMessagesMiddleware",
"simplecasts.middleware.HtmxRedirectMiddleware",
"simplecasts.middleware.PlayerMiddleware",
"simplecasts.middleware.SearchMiddleware",
"simplecasts.episodes.middleware.PlayerMiddleware",
]

# Databases
Expand Down Expand Up @@ -216,7 +214,7 @@
# authentication settings
# https://docs.djangoproject.com/en/dev/ref/settings/#authentication-backends

AUTH_USER_MODEL = "users.User"
AUTH_USER_MODEL = "simplecasts.User"

AUTHENTICATION_BACKENDS = [
"django.contrib.auth.backends.ModelBackend",
Expand Down
19 changes: 1 addition & 18 deletions config/urls.py
Original file line number Diff line number Diff line change
Expand Up @@ -2,25 +2,8 @@
from django.contrib import admin
from django.urls import include, path

from simplecasts import views

urlpatterns = [
path("", views.index, name="index"),
path("about/", views.about, name="about"),
path("privacy/", views.privacy, name="privacy"),
path("accept-cookies/", views.accept_cookies, name="accept_cookies"),
path(
"covers/<int:size>/<str:encoded_url>.webp",
views.cover_image,
name="cover_image",
),
path("robots.txt", views.robots, name="robots"),
path("manifest.json", views.manifest, name="manifest"),
path(".well-known/assetlinks.json", views.assetlinks, name="assetlinks"),
path(".well-known/security.txt", views.security, name="security"),
path("", include("simplecasts.episodes.urls")),
path("", include("simplecasts.podcasts.urls")),
path("", include("simplecasts.users.urls")),
path("", include("simplecasts.urls")),
path("account/", include("allauth.urls")),
path("ht/", include("health_check.urls")),
path(settings.ADMIN_URL, admin.site.urls),
Expand Down
3 changes: 0 additions & 3 deletions conftest.py
Original file line number Diff line number Diff line change
@@ -1,6 +1,3 @@
pytest_plugins = [
"simplecasts.tests.fixtures",
"simplecasts.episodes.tests.fixtures",
"simplecasts.podcasts.tests.fixtures",
"simplecasts.users.tests.fixtures",
]
16 changes: 8 additions & 8 deletions justfile
Original file line number Diff line number Diff line change
Expand Up @@ -16,11 +16,11 @@ export USE_X_FORWARDED_HOST := "false"

# Install all dependencies
[group('development')]
install: pyinstall precommitinstall nltkdownload
install: pyinstall pcinstall nltkdownload

# Update all dependencies
[group('development')]
update: pyupdate pyinstall precommitupdate
update: pyupdate pyinstall pcupdate

# Install all Python dependencies
[group('development')]
Expand Down Expand Up @@ -59,7 +59,7 @@ typecheck *args:

# Run all checks
[group('development')]
check: precommitall typecheck test
check: pcrunall typecheck test

# Download NLTK data
[group('development')]
Expand Down Expand Up @@ -88,24 +88,24 @@ psql *args:

# Run pre-commit manually
[group('development')]
precommit *args:
pc *args:
uv run --with pre-commit-uv pre-commit {{ args }}

# Install pre-commit hooks
[group('development')]
precommitinstall:
pcinstall:
@just precommit install
@just precommit install --hook-type commit-msg

# Update pre-commit hooks
[group('development')]
precommitupdate:
pcupdate:
@just precommit autoupdate

# Re-run pre-commit on all files
[group('development')]
precommitall:
@just precommit run --all-files
pcrunall:
@just pc run --all-files

# Run Ansible playbook
[group('deployment')]
Expand Down
2 changes: 1 addition & 1 deletion pyproject.toml
Original file line number Diff line number Diff line change
Expand Up @@ -195,7 +195,7 @@ django_settings_module = "simplecasts.settings"
include=["simplecasts"]
exclude=[
"**/migrations/*.py",
"**/tests/*.py",
"**/tests/**",
]

typeCheckingMode = "basic"
109 changes: 100 additions & 9 deletions simplecasts/podcasts/admin.py → simplecasts/admin.py
Original file line number Diff line number Diff line change
@@ -1,19 +1,24 @@
from typing import TYPE_CHECKING, ClassVar

from django.contrib import admin
from django.contrib.auth.admin import UserAdmin as BaseUserAdmin
from django.db.models import Count, Exists, OuterRef, QuerySet
from django.http import HttpRequest
from django.template.defaultfilters import truncatechars
from django.utils import timezone
from django.utils.timesince import timesince, timeuntil

from simplecasts.podcasts.models import (
from simplecasts.models import (
AudioLog,
Category,
Episode,
Podcast,
PodcastQuerySet,
Recommendation,
Subscription,
User,
)
from simplecasts.search import search_queryset
from simplecasts.models.episodes import EpisodeQuerySet
from simplecasts.models.podcasts import PodcastQuerySet

if TYPE_CHECKING:
from django_stubs_ext import StrOrPromise # pragma: no cover
Expand All @@ -27,6 +32,21 @@ class CategoryWithNumPodcasts(Category):
CategoryWithNumPodcasts = Category


@admin.register(User)
class UserAdmin(BaseUserAdmin):
"""User model admin."""

fieldsets = (
*tuple(BaseUserAdmin.fieldsets or ()),
(
"User preferences",
{
"fields": ("send_email_notifications",),
},
),
)


@admin.register(Category)
class CategoryAdmin(admin.ModelAdmin):
"""Admin for podcast categories."""
Expand Down Expand Up @@ -228,7 +248,7 @@ class PodcastAdmin(admin.ModelAdmin):
"exception",
)

search_fields = ("search_vector",)
search_fields = ("search_document",)

fieldsets = (
(
Expand Down Expand Up @@ -294,11 +314,9 @@ def get_search_results(
"""Search episodes."""
return (
(
search_queryset(
queryset,
search_term,
*self.search_fields,
).order_by("-rank", "-pub_date"),
queryset.search(search_term, *self.search_fields).order_by(
"-rank", "-pub_date"
),
False,
)
if search_term
Expand Down Expand Up @@ -352,3 +370,76 @@ class SubscriptionAdmin(admin.ModelAdmin):
def get_queryset(self, request: HttpRequest) -> QuerySet[Subscription]:
"""Returns queryset with related fields."""
return super().get_queryset(request).select_related("podcast", "subscriber")


@admin.register(Episode)
class EpisodeAdmin(admin.ModelAdmin):
"""Django admin for Episode model."""

list_display = ("episode_title", "podcast_title", "pub_date")
list_select_related = ("podcast",)
raw_id_fields = ("podcast",)
search_fields = ("search_document",)

@admin.display(description="Title")
def episode_title(self, obj: Episode) -> str:
"""Render truncated episode title."""
return truncatechars(obj.title, 30)

@admin.display(description="Podcast")
def podcast_title(self, obj: Episode) -> str:
"""Render truncated podcast title."""
return truncatechars(obj.podcast.title, 30)

def get_search_results(
self,
request: HttpRequest,
queryset: EpisodeQuerySet,
search_term: str,
) -> tuple[QuerySet[Episode], bool]:
"""Search episodes."""
return (
(
queryset.search(search_term, *self.search_fields).order_by(
"-rank", "-pub_date"
),
False,
)
if search_term
else super().get_search_results(request, queryset, search_term)
)

def get_ordering(self, request: HttpRequest) -> list[str]:
"""Returns optimized search ordering.

If unfiltered, just search by id.
"""
return (
[]
if request.GET.get("q")
else [
"-id",
]
)


@admin.register(AudioLog)
class AudioLogAdmin(admin.ModelAdmin):
"""Django admin for AudioLog model."""

list_display = (
"episode",
"user",
)
readonly_fields = (
"episode",
"user",
"current_time",
"duration",
"listened",
)
ordering = ("-listened",)

def get_queryset(self, request: HttpRequest) -> QuerySet[AudioLog]:
"""Optimize queryset for admin."""
return super().get_queryset(request).select_related("episode", "user")
4 changes: 2 additions & 2 deletions simplecasts/podcasts/apps.py → simplecasts/apps.py
Original file line number Diff line number Diff line change
@@ -1,8 +1,8 @@
from django.apps import AppConfig


class PodcastsConfig(AppConfig):
class ProjectConfig(AppConfig):
"""App configuration."""

name = "simplecasts.podcasts"
name = "simplecasts"
default_auto_field = "django.db.models.BigAutoField"
82 changes: 0 additions & 82 deletions simplecasts/episodes/admin.py

This file was deleted.

6 changes: 0 additions & 6 deletions simplecasts/episodes/apps.py

This file was deleted.

Loading
Loading