From 603090205a45830035bb10d4c289a840fcfc9c18 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Joakim=20Helle=C5=9Ben?= Date: Fri, 27 Mar 2026 04:26:09 +0100 Subject: [PATCH] Use templates for rendering --- feeds/urls.py | 22 ++++- feeds/views.py | 63 ++++--------- templates/base.html | 148 ++++++++++++++++++++++++++---- templates/feeds/entry_detail.html | 34 +++++++ templates/feeds/feed_detail.html | 49 ++++++++++ templates/feeds/feed_list.html | 2 +- 6 files changed, 247 insertions(+), 71 deletions(-) create mode 100644 templates/feeds/entry_detail.html create mode 100644 templates/feeds/feed_detail.html diff --git a/feeds/urls.py b/feeds/urls.py index 163c7f0..be9e7e5 100644 --- a/feeds/urls.py +++ b/feeds/urls.py @@ -10,9 +10,25 @@ if TYPE_CHECKING: urlpatterns: list[URLPattern | URLResolver] = [ - path("", views.home, name="home"), - path("feeds/", views.feed_list, name="feed-list"), - path("feeds//", views.feed_detail, name="feed-detail"), + # / + path( + "", + views.home, + name="home", + ), + # /feeds/ + path( + "feeds/", + views.feed_list, + name="feeds", + ), + # /feeds// + path( + "feeds//", + views.feed_detail, + name="details", + ), + # /feeds//entries// path( "feeds//entries//", views.entry_detail, diff --git a/feeds/views.py b/feeds/views.py index 5780b22..e2331a9 100644 --- a/feeds/views.py +++ b/feeds/views.py @@ -4,7 +4,7 @@ import html import json from typing import TYPE_CHECKING -from django.http import HttpResponse +from django.db.models.query import QuerySet from django.shortcuts import get_object_or_404 from django.shortcuts import redirect from django.shortcuts import render @@ -14,6 +14,7 @@ from feeds.models import Feed if TYPE_CHECKING: from django.http import HttpRequest + from django.http import HttpResponse from pytest_django.asserts import QuerySet @@ -38,35 +39,16 @@ def feed_detail(request: HttpRequest, feed_id: int) -> HttpResponse: HttpResponse: An HTML response containing the feed details and its entries. """ feed: Feed = get_object_or_404(Feed, id=feed_id) - entries: QuerySet[Entry, Entry] = Entry.objects.filter(feed=feed).order_by( "-published_at", "-fetched_at", )[:50] - html: list[str] = [ - "", - f"FeedVault - {feed.url}", - "

Feed Detail

", - f"

URL: {feed.url}

", - f"

Domain: {feed.domain}

", - f"

Active: {'yes' if feed.is_active else 'no'}

", - f"

Created: {feed.created_at}

", - f"

Last fetched: {feed.last_fetched_at}

", - "

Entries (latest 50)

", - "
    ", - ] - for entry in entries: - title: str | None = entry.data.get("title") if entry.data else None - summary: str | None = entry.data.get("summary") if entry.data else None - snippet: str = title or summary or "[no title]" - html.append( - f"
  • {entry.published_at or entry.fetched_at}: " - f'{snippet} ' - f"(id: {entry.entry_id})
  • ", - ) - html.extend(("
", '

Back to list

', "")) - return HttpResponse("\n".join(html)) + context: dict[str, Feed | QuerySet[Entry, Entry]] = { + "feed": feed, + "entries": entries, + } + return render(request, "feeds/feed_detail.html", context) def entry_detail(request: HttpRequest, feed_id: int, entry_id: int) -> HttpResponse: @@ -83,32 +65,19 @@ def entry_detail(request: HttpRequest, feed_id: int, entry_id: int) -> HttpRespo feed: Feed = get_object_or_404(Feed, id=feed_id) entry: Entry = get_object_or_404(Entry, id=entry_id, feed=feed) - # Render images if present in entry.data - entry_data_html: str = "" + # Prepare entry data for display if entry.data: formatted_json: str = json.dumps(entry.data, indent=2, ensure_ascii=False) - escaped_json: str = html.escape(formatted_json) - note: str = '
Note: HTML in the JSON is escaped for display and will not be rendered as HTML.
' - entry_data_html: str = f"{note}
{escaped_json}
" + escaped_json: str | None = html.escape(formatted_json) else: - entry_data_html: str = "

[No data]

" + escaped_json: str | None = None - html_lines: list[str] = [ - "", - f"FeedVault - Entry {entry.entry_id}", - "

Entry Detail

", - f"

Feed: {feed.url}

", - f"

Entry ID: {entry.entry_id}

", - f"

Published: {entry.published_at}

", - f"

Fetched: {entry.fetched_at}

", - f"

Content Hash: {entry.content_hash}

", - f"

Error Message: {entry.error_message or '[none]'}

", - "

Entry Data

", - entry_data_html, - f'

Back to feed

', - "", - ] - return HttpResponse("\n".join(html_lines)) + context = { + "feed": feed, + "entry": entry, + "escaped_json": escaped_json, + } + return render(request, "feeds/entry_detail.html", context) def home(request: HttpRequest) -> HttpResponse: diff --git a/templates/base.html b/templates/base.html index ae5836d..690546f 100644 --- a/templates/base.html +++ b/templates/base.html @@ -3,33 +3,141 @@ - - - {% block title %} - FeedVault - {% endblock title %} - + {% if description %}{% endif %} + {% if keywords %}{% endif %} + {% if author %}{% endif %} + {% if canonical %}{% endif %} + {{ title|default:"FeedVault" }} + + {% if messages %} + + {% endif %}
-

FeedVault

- +

+ FeedVault +

+
+
+ Archive of + web feeds. + {{ stats }} + +
+
+ +
+
+ +
- {% block content %} - {% endblock content %} + {% block content %}{% endblock %}
+
-

Web scraping is not a crime. - No rights reserved. - A birthday present for Plipp ❤️

+ +
+
Web scraping is not a crime.
+
No rights reserved.
+
+
+
TheLovinator#9276 on Discord
+
A birthday present for Plipp ❤️
+
+
diff --git a/templates/feeds/entry_detail.html b/templates/feeds/entry_detail.html new file mode 100644 index 0000000..2adb2c7 --- /dev/null +++ b/templates/feeds/entry_detail.html @@ -0,0 +1,34 @@ +{% extends "base.html" %} +{% block content %} +

Entry Detail

+

+ Feed: {{ feed.url }} +

+

+ Entry ID: {{ entry.entry_id }} +

+

+ Published: {{ entry.published_at }} +

+

+ Fetched: {{ entry.fetched_at }} +

+

+ Content Hash: {{ entry.content_hash }} +

+

+ Error Message: {{ entry.error_message|default:"[none]" }} +

+

Entry Data

+ {% if escaped_json %} +
+ Note: HTML in the JSON is escaped for display and will not be rendered as HTML. +
+
{{ escaped_json|safe }}
+ {% else %} +

[No data]

+ {% endif %} +

+ Back to feed +

+{% endblock content %} diff --git a/templates/feeds/feed_detail.html b/templates/feeds/feed_detail.html new file mode 100644 index 0000000..9c4571c --- /dev/null +++ b/templates/feeds/feed_detail.html @@ -0,0 +1,49 @@ +{% extends "base.html" %} +{% block content %} +

Feed Detail

+

+ URL: {{ feed.url }} +

+

+ Domain: {{ feed.domain }} +

+

+ Active: {{ feed.is_active|yesno:"yes,no" }} +

+

+ Created: {{ feed.created_at }} +

+

+ Last fetched: {{ feed.last_fetched_at }} +

+

Entries (latest 50)

+ + + + + + + + + {% for entry in entries %} + + + + + {% endfor %} + +
Title/DescriptionEntry ID
+ + {{ entry.data.title|default:entry.data.description|default:"[no title]" }} + +
+ {{ entry.published_at|default:entry.fetched_at }} +
+ + {{ entry.entry_id|cut:'https://'|truncatechars:50 }} + +
+

+ Back to list +

+{% endblock content %} diff --git a/templates/feeds/feed_list.html b/templates/feeds/feed_list.html index d6c9e55..d40f8c3 100644 --- a/templates/feeds/feed_list.html +++ b/templates/feeds/feed_list.html @@ -4,7 +4,7 @@