Add search
This commit is contained in:
parent
b5b240fb99
commit
942965d262
5 changed files with 90 additions and 3 deletions
|
|
@ -244,3 +244,50 @@ class TestStats(TestCase):
|
||||||
response: str = get_db_size()
|
response: str = get_db_size()
|
||||||
assert isinstance(response, str), f"Expected a string, got {response}"
|
assert isinstance(response, str), f"Expected a string, got {response}"
|
||||||
assert "kB" in response, f"Expected 'kB' in response, got {response}"
|
assert "kB" in response, f"Expected 'kB' in response, got {response}"
|
||||||
|
|
||||||
|
|
||||||
|
class TestSearch(TestCase):
|
||||||
|
def setUp(self) -> None:
|
||||||
|
"""Create a test feed."""
|
||||||
|
self.domain: Domain = Domain.objects.create(
|
||||||
|
name="feedvault",
|
||||||
|
url="feedvault.se",
|
||||||
|
)
|
||||||
|
self.user: User = User.objects.create_user(
|
||||||
|
username="testuser",
|
||||||
|
email="hello@feedvault.se",
|
||||||
|
password="testpassword", # noqa: S106
|
||||||
|
)
|
||||||
|
self.feed: Feed = Feed.objects.create(
|
||||||
|
user=self.user,
|
||||||
|
bozo=False,
|
||||||
|
feed_url="https://feedvault.se/feed.xml",
|
||||||
|
domain=self.domain,
|
||||||
|
)
|
||||||
|
|
||||||
|
def test_search_page(self) -> None:
|
||||||
|
"""Test if the search page is accessible."""
|
||||||
|
response: HttpResponse = self.client.get(reverse("search"))
|
||||||
|
assert response.status_code == 200, f"Expected 200, got {response.status_code}"
|
||||||
|
|
||||||
|
def test_search_page_search(self) -> None:
|
||||||
|
"""Search for a term that doesn't exist."""
|
||||||
|
response: HttpResponse = self.client.get(reverse("search"), {"q": "test"})
|
||||||
|
assert response.status_code == 200, f"Expected 200, got {response.status_code}"
|
||||||
|
assert (
|
||||||
|
"No results found" in response.content.decode()
|
||||||
|
), f"Expected 'No results found' in response, got {response.content}"
|
||||||
|
|
||||||
|
def test_search_page_search_found(self) -> None:
|
||||||
|
"""Search for a term that exists."""
|
||||||
|
response: HttpResponse = self.client.get(reverse("search"), {"q": "feedvault"})
|
||||||
|
assert response.status_code == 200, f"Expected 200, got {response.status_code}"
|
||||||
|
assert "feedvault" in response.content.decode(), f"Expected 'feedvault' in response, got {response.content}"
|
||||||
|
|
||||||
|
def test_search_page_search_empty(self) -> None:
|
||||||
|
"""Search for an empty term. This should redirect to the feeds page."""
|
||||||
|
response: HttpResponse = self.client.get(reverse("search"), {"q": ""})
|
||||||
|
assert response.status_code == 200, f"Expected 302, got {response.status_code}"
|
||||||
|
assert (
|
||||||
|
"Latest Feeds" in response.content.decode()
|
||||||
|
), f"Expected 'Latest Feeds' in response, got {response.content}"
|
||||||
|
|
|
||||||
|
|
@ -36,6 +36,7 @@ urlpatterns: list = [
|
||||||
{"sitemaps": sitemaps},
|
{"sitemaps": sitemaps},
|
||||||
name="django.contrib.sitemaps.views.sitemap",
|
name="django.contrib.sitemaps.views.sitemap",
|
||||||
),
|
),
|
||||||
|
path(route="search/", view=views.SearchView.as_view(), name="search"),
|
||||||
path(route="domains/", view=views.DomainsView.as_view(), name="domains"),
|
path(route="domains/", view=views.DomainsView.as_view(), name="domains"),
|
||||||
path(route="domain/<int:domain_id>/", view=views.DomainView.as_view(), name="domain"),
|
path(route="domain/<int:domain_id>/", view=views.DomainView.as_view(), name="domain"),
|
||||||
path("api/v1/", api_v1.urls), # type: ignore # noqa: PGH003
|
path("api/v1/", api_v1.urls), # type: ignore # noqa: PGH003
|
||||||
|
|
|
||||||
|
|
@ -43,7 +43,7 @@ class IndexView(View):
|
||||||
def get(self, request: HttpRequest) -> HttpResponse:
|
def get(self, request: HttpRequest) -> HttpResponse:
|
||||||
"""Load the index page."""
|
"""Load the index page."""
|
||||||
template = loader.get_template(template_name="index.html")
|
template = loader.get_template(template_name="index.html")
|
||||||
context = {
|
context: dict[str, str] = {
|
||||||
"description": "FeedVault allows users to archive and search their favorite web feeds.",
|
"description": "FeedVault allows users to archive and search their favorite web feeds.",
|
||||||
"keywords": "feed, rss, atom, archive, rss list",
|
"keywords": "feed, rss, atom, archive, rss list",
|
||||||
"author": "TheLovinator",
|
"author": "TheLovinator",
|
||||||
|
|
@ -95,7 +95,7 @@ class FeedsView(View):
|
||||||
|
|
||||||
context: dict[str, str | Page | int] = {
|
context: dict[str, str | Page | int] = {
|
||||||
"feeds": pages,
|
"feeds": pages,
|
||||||
"description": "An archive of all feeds",
|
"description": "An archive of web feeds",
|
||||||
"keywords": "feed, rss, atom, archive, rss list",
|
"keywords": "feed, rss, atom, archive, rss list",
|
||||||
"author": "TheLovinator",
|
"author": "TheLovinator",
|
||||||
"canonical": "https://feedvault.se/feeds/",
|
"canonical": "https://feedvault.se/feeds/",
|
||||||
|
|
@ -450,3 +450,27 @@ class DomainView(View):
|
||||||
}
|
}
|
||||||
|
|
||||||
return render(request, "domain.html", context)
|
return render(request, "domain.html", context)
|
||||||
|
|
||||||
|
|
||||||
|
class SearchView(View):
|
||||||
|
"""Search view."""
|
||||||
|
|
||||||
|
def get(self, request: HtmxHttpRequest) -> HttpResponse:
|
||||||
|
"""Load the search page."""
|
||||||
|
query: str | None = request.GET.get("q", None)
|
||||||
|
if not query:
|
||||||
|
return FeedsView().get(request)
|
||||||
|
|
||||||
|
feeds: BaseManager[Feed] = Feed.objects.filter(feed_url__icontains=query).order_by("-created_at")[:100]
|
||||||
|
|
||||||
|
context = {
|
||||||
|
"feeds": feeds,
|
||||||
|
"description": f"Search results for {query}",
|
||||||
|
"keywords": f"feed, rss, atom, archive, rss list, {query}",
|
||||||
|
"author": "TheLovinator",
|
||||||
|
"canonical": f"https://feedvault.se/search/?q={query}",
|
||||||
|
"title": f"Search results for {query}",
|
||||||
|
"query": query,
|
||||||
|
}
|
||||||
|
|
||||||
|
return render(request, "search.html", context)
|
||||||
|
|
|
||||||
|
|
@ -96,7 +96,7 @@
|
||||||
</small>
|
</small>
|
||||||
</div>
|
</div>
|
||||||
<div class="right">
|
<div class="right">
|
||||||
<form action="#" method="get">
|
<form action="{% url 'search' %}" method="get">
|
||||||
<input type="text" name="q" placeholder="Search" />
|
<input type="text" name="q" placeholder="Search" />
|
||||||
<button type="submit">Search</button>
|
<button type="submit">Search</button>
|
||||||
</form>
|
</form>
|
||||||
|
|
|
||||||
15
templates/search.html
Normal file
15
templates/search.html
Normal file
|
|
@ -0,0 +1,15 @@
|
||||||
|
{% extends "base.html" %}
|
||||||
|
{% block content %}
|
||||||
|
<h2>
|
||||||
|
Searched for:
|
||||||
|
"{{ query|default:"Search" }}"
|
||||||
|
</h2>
|
||||||
|
{% if feeds %}
|
||||||
|
{% for feed in feeds %}
|
||||||
|
<a href="{% url 'feed' feed.id %}">{{ feed.feed_url|default:"Unknown Feed" }} →</a>
|
||||||
|
<br>
|
||||||
|
{% endfor %}
|
||||||
|
{% else %}
|
||||||
|
<p>No results found.</p>
|
||||||
|
{% endif %}
|
||||||
|
{% endblock %}
|
||||||
Loading…
Add table
Add a link
Reference in a new issue