/", views.feed_detail, name="feed-detail"),
]
diff --git a/feeds/views.py b/feeds/views.py
index e69de29..38a5d93 100644
--- a/feeds/views.py
+++ b/feeds/views.py
@@ -0,0 +1,70 @@
+from typing import TYPE_CHECKING
+
+from django.http import HttpResponse
+from django.shortcuts import get_object_or_404
+
+from feeds.models import Entry
+from feeds.models import Feed
+
+if TYPE_CHECKING:
+ from django.http import HttpRequest
+ from pytest_django.asserts import QuerySet
+
+
+def feed_list(request: HttpRequest) -> HttpResponse:
+ """View to list all feeds.
+
+ Returns:
+ HttpResponse: An HTML response containing the list of feeds.
+ """
+ feeds = Feed.objects.all().order_by("id")
+ html = [
+ "",
+ "FeedVault - Feeds",
+ "Feed List
",
+ "",
+ ]
+ html.extend(
+ f'- {feed.url}
' for feed in feeds
+ )
+ html.extend(("
", ""))
+ return HttpResponse("\n".join(html))
+
+
+def feed_detail(request: HttpRequest, feed_id: int) -> HttpResponse:
+ """View to display the details of a specific feed.
+
+ Args:
+ request (HttpRequest): The HTTP request object.
+ feed_id (int): The ID of the feed to display.
+
+ Returns:
+ 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}: {snippet} (id: {entry.entry_id})
",
+ )
+ html.extend(("
", 'Back to list
', ""))
+ return HttpResponse("\n".join(html))
diff --git a/pyproject.toml b/pyproject.toml
index 3660414..6f2ca32 100644
--- a/pyproject.toml
+++ b/pyproject.toml
@@ -11,18 +11,24 @@ dependencies = [
"django-celery-results",
"django-debug-toolbar",
"django-silk[formatting]",
+ "django-stubs-ext",
"django",
"flower",
"gunicorn",
"hiredis",
"index-now-for-python",
+ "niquests",
"platformdirs",
"psycopg[binary]",
+ "pydantic",
"python-dotenv",
"redis",
"sentry-sdk",
"setproctitle",
"sitemap-parser",
+ "xmltodict",
+ "dateparser>=1.3.0",
+ "xxhash>=3.6.0",
]
[dependency-groups]
@@ -36,6 +42,7 @@ dev = [
"pytest-randomly",
"pytest-xdist[psutil]",
"pytest",
+ "types-xmltodict",
]
[tool.pytest.ini_options]
DJANGO_SETTINGS_MODULE = "config.settings"