diff --git a/feeds/management/commands/archive_feed.py b/feeds/management/commands/archive_feed.py index 85f3b18..3f5a500 100644 --- a/feeds/management/commands/archive_feed.py +++ b/feeds/management/commands/archive_feed.py @@ -56,18 +56,14 @@ class Command(BaseCommand): if count == 0: msg = f"No entries found for feed: {url}" self.stdout.write(self.style.WARNING(msg)) + else: - proceed: bool = True if not force: - proceed: bool = self.confirm_and_list_entries( - url=url, - entries_qs=entries_qs, - count=count, - ) - if proceed: - entries_qs.delete() - msg = f"Deleted {count} entries for feed: {url}" - self.stdout.write(self.style.SUCCESS(msg)) + return self.confirm_and_list_entries(url, entries_qs, count) + + entries_qs.delete() + msg = f"Deleted {count} entries for feed: {url}" + self.stdout.write(self.style.SUCCESS(msg)) new_entries: int = fetch_and_archive_feed(feed) if new_entries: @@ -77,23 +73,15 @@ class Command(BaseCommand): else: msg: str = "\tFeed is up to date, but no new entries were archived." self.stdout.write(self.style.WARNING(msg)) + return None def confirm_and_list_entries( self, url: str, entries_qs: QuerySet[Entry, Entry], count: int, - ) -> bool: - """Confirm with the user before deleting entries and list some of them. - - Args: - url (str): The URL of the feed. - entries_qs (QuerySet[Entry, Entry]): The queryset of entries to be deleted. - count (int): The total number of entries to be deleted. - - Returns: - True if user confirms, False otherwise. - """ + ) -> None: + """Confirm with the user before deleting entries and list some of them.""" msg: str = f"The following {count} entries will be removed for feed: {url}" self.stdout.write(self.style.WARNING(msg)) @@ -106,8 +94,7 @@ class Command(BaseCommand): confirm: str = input("Are you sure you want to proceed? (yes/no): ") if confirm.lower() != "yes": self.stdout.write(self.style.ERROR("Operation cancelled.")) - return False - return True + return def get_entry_title(entry: Entry) -> str | None: diff --git a/feeds/urls.py b/feeds/urls.py index be9e7e5..163c7f0 100644 --- a/feeds/urls.py +++ b/feeds/urls.py @@ -10,25 +10,9 @@ if TYPE_CHECKING: urlpatterns: list[URLPattern | URLResolver] = [ - # / - path( - "", - views.home, - name="home", - ), - # /feeds/ - path( - "feeds/", - views.feed_list, - name="feeds", - ), - # /feeds// - path( - "feeds//", - views.feed_detail, - name="details", - ), - # /feeds//entries// + path("", views.home, name="home"), + path("feeds/", views.feed_list, name="feed-list"), + path("feeds//", views.feed_detail, name="feed-detail"), path( "feeds//entries//", views.entry_detail, diff --git a/feeds/views.py b/feeds/views.py index e2331a9..5780b22 100644 --- a/feeds/views.py +++ b/feeds/views.py @@ -4,7 +4,7 @@ import html import json from typing import TYPE_CHECKING -from django.db.models.query import QuerySet +from django.http import HttpResponse from django.shortcuts import get_object_or_404 from django.shortcuts import redirect from django.shortcuts import render @@ -14,7 +14,6 @@ from feeds.models import Feed if TYPE_CHECKING: from django.http import HttpRequest - from django.http import HttpResponse from pytest_django.asserts import QuerySet @@ -39,16 +38,35 @@ 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] - context: dict[str, Feed | QuerySet[Entry, Entry]] = { - "feed": feed, - "entries": entries, - } - return render(request, "feeds/feed_detail.html", context) + 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)) def entry_detail(request: HttpRequest, feed_id: int, entry_id: int) -> HttpResponse: @@ -65,19 +83,32 @@ 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) - # Prepare entry data for display + # Render images if present in entry.data + entry_data_html: str = "" if entry.data: formatted_json: str = json.dumps(entry.data, indent=2, ensure_ascii=False) - escaped_json: str | None = html.escape(formatted_json) + 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}
" else: - escaped_json: str | None = None + entry_data_html: str = "

[No data]

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

- FeedVault -

+

FeedVault

+
-
-
- Archive of - web feeds. - {{ stats }} - -
-
- -
-
- -
- {% block content %}{% endblock %} + {% block content %} + {% endblock content %}
-
- -
-
Web scraping is not a crime.
-
No rights reserved.
-
-
-
TheLovinator#9276 on Discord
-
A birthday present for Plipp ❤️
-
-
+

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

diff --git a/templates/feeds/entry_detail.html b/templates/feeds/entry_detail.html deleted file mode 100644 index 2adb2c7..0000000 --- a/templates/feeds/entry_detail.html +++ /dev/null @@ -1,34 +0,0 @@ -{% 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 deleted file mode 100644 index 9c4571c..0000000 --- a/templates/feeds/feed_detail.html +++ /dev/null @@ -1,49 +0,0 @@ -{% 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 d40f8c3..d6c9e55 100644 --- a/templates/feeds/feed_list.html +++ b/templates/feeds/feed_list.html @@ -4,7 +4,7 @@