hide search category
This commit is contained in:
parent
3de032367a
commit
a5d73e864e
12 changed files with 149 additions and 67 deletions
|
@ -45,7 +45,10 @@ def search_item(
|
|||
if not query:
|
||||
return 400, {"message": "Invalid query"}
|
||||
items, num_pages, count, _ = query_index(
|
||||
query, page=page, category=category, prepare_external=False
|
||||
query,
|
||||
page=page,
|
||||
categories=[category] if category else None,
|
||||
prepare_external=False,
|
||||
)
|
||||
return 200, {"data": items, "pages": num_pages, "count": count}
|
||||
|
||||
|
|
|
@ -18,7 +18,7 @@ _logger = logging.getLogger(__name__)
|
|||
|
||||
class DbIndexer:
|
||||
@classmethod
|
||||
def search(cls, q, page=1, category=None, tag=None, sort=None):
|
||||
def search(cls, q, page=1, categories=None, tag=None, sort=None):
|
||||
result = lambda: None
|
||||
result.items = Item.objects.filter(title__contains=q)[:10]
|
||||
result.num_pages = 1
|
||||
|
@ -47,14 +47,14 @@ else:
|
|||
Indexer = DbIndexer
|
||||
|
||||
|
||||
def query_index(keywords, category=None, tag=None, page=1, prepare_external=True):
|
||||
def query_index(keywords, categories=None, tag=None, page=1, prepare_external=True):
|
||||
if (
|
||||
page < 1
|
||||
or page > 99
|
||||
or (not tag and isinstance(keywords, str) and len(keywords) < 2)
|
||||
):
|
||||
return [], 0, 0, []
|
||||
result = Indexer.search(keywords, page=page, category=category, tag=tag)
|
||||
result = Indexer.search(keywords, page=page, categories=categories, tag=tag)
|
||||
keys = set()
|
||||
items = []
|
||||
duplicated_items = []
|
||||
|
@ -92,7 +92,7 @@ def query_index(keywords, category=None, tag=None, page=1, prepare_external=True
|
|||
|
||||
if prepare_external:
|
||||
# store site url to avoid dups in external search
|
||||
cache_key = f"search_{category}_{keywords}"
|
||||
cache_key = f"search_{','.join(categories or [])}_{keywords}"
|
||||
urls = list(set(cache.get(cache_key, []) + urls))
|
||||
cache.set(cache_key, urls, timeout=300)
|
||||
|
||||
|
|
|
@ -311,13 +311,10 @@ class Indexer:
|
|||
logger.warn(f"delete item error: \n{e}")
|
||||
|
||||
@classmethod
|
||||
def search(cls, q, page=1, category=None, tag=None, sort=None):
|
||||
def search(cls, q, page=1, categories=None, tag=None, sort=None):
|
||||
f = []
|
||||
if category:
|
||||
if category == "movietv":
|
||||
f.append("category:= [movie,tv]")
|
||||
else:
|
||||
f.append("category:= " + category)
|
||||
if categories:
|
||||
f.append(f"category:= [{','.join(categories)}]")
|
||||
if tag:
|
||||
f.append(f"tags:= '{tag}'")
|
||||
filters = " && ".join(f)
|
||||
|
|
|
@ -5,7 +5,7 @@ from django.shortcuts import render, redirect
|
|||
from django.contrib.auth.decorators import login_required
|
||||
from django.utils.translation import gettext_lazy as _
|
||||
from django.http import HttpResponseRedirect
|
||||
from catalog.common.models import SiteName
|
||||
from catalog.common.models import ItemCategory, SiteName
|
||||
from catalog.common.sites import AbstractSite, SiteManager
|
||||
from ..models import *
|
||||
from django.conf import settings
|
||||
|
@ -82,10 +82,27 @@ def fetch(request, url, is_refetch: bool = False, site: AbstractSite | None = No
|
|||
)
|
||||
|
||||
|
||||
def visible_categories(request):
|
||||
vc = request.session.get("p_categories", None)
|
||||
if vc is None:
|
||||
vc = [
|
||||
x
|
||||
for x in item_categories()
|
||||
if x.value not in request.user.preference.hidden_categories
|
||||
]
|
||||
request.session["p_categories"] = vc
|
||||
return vc
|
||||
|
||||
|
||||
def search(request):
|
||||
category = request.GET.get("c", default="all").strip().lower()
|
||||
if category == "all":
|
||||
if category == "all" or not category:
|
||||
category = None
|
||||
categories = visible_categories(request)
|
||||
elif category == "movietv":
|
||||
categories = [ItemCategory.Movie, ItemCategory.TV]
|
||||
else:
|
||||
categories = [ItemCategory(category)]
|
||||
keywords = request.GET.get("q", default="").strip()
|
||||
tag = request.GET.get("tag", default="").strip()
|
||||
p = request.GET.get("page", default="1")
|
||||
|
@ -105,7 +122,7 @@ def search(request):
|
|||
if site:
|
||||
return fetch(request, keywords, False, site)
|
||||
|
||||
items, num_pages, _, dup_items = query_index(keywords, category, tag, p)
|
||||
items, num_pages, _, dup_items = query_index(keywords, categories, tag, p)
|
||||
return render(
|
||||
request,
|
||||
"search_results.html",
|
||||
|
|
|
@ -4,6 +4,7 @@
|
|||
{% load humanize %}
|
||||
{% load admin_url %}
|
||||
{% load mastodon %}
|
||||
{% load duration %}
|
||||
{% load oauth_token %}
|
||||
{% load truncate %}
|
||||
{% load highlight %}
|
||||
|
@ -26,46 +27,59 @@
|
|||
<hgroup>
|
||||
<h5>“{{ request.GET.q }}” {% trans '的搜索结果' %}</h5>
|
||||
<div>
|
||||
{% visible_categories as cats %}
|
||||
{% if request.GET.c and request.GET.c != 'all' %}
|
||||
<a href="?q={{ request.GET.q }}&c=all">全部</a>
|
||||
<a href="?q={{ request.GET.q }}&c=all">全部</a>
|
||||
{% else %}
|
||||
全部
|
||||
{% endif %}
|
||||
|
|
||||
{% if request.GET.c != 'book' %}
|
||||
<a href="?q={{ request.GET.q }}&c=book">书籍</a>
|
||||
{% else %}
|
||||
书籍
|
||||
{% if 'book' in cats %}
|
||||
|
|
||||
{% if request.GET.c != 'book' %}
|
||||
<a href="?q={{ request.GET.q }}&c=book">书籍</a>
|
||||
{% else %}
|
||||
书籍
|
||||
{% endif %}
|
||||
{% endif %}
|
||||
|
|
||||
{% if request.GET.c != 'movietv' %}
|
||||
<a href="?q={{ request.GET.q }}&c=movietv">影视</a>
|
||||
{% else %}
|
||||
影视
|
||||
{% if 'movie' in cats or 'tv' in cats %}
|
||||
|
|
||||
{% if request.GET.c != 'movietv' %}
|
||||
<a href="?q={{ request.GET.q }}&c=movietv">影视</a>
|
||||
{% else %}
|
||||
影视
|
||||
{% endif %}
|
||||
{% endif %}
|
||||
|
|
||||
{% if request.GET.c != 'podcast' %}
|
||||
<a href="?q={{ request.GET.q }}&c=podcast">播客</a>
|
||||
{% else %}
|
||||
播客
|
||||
{% if 'podcast' in cats %}
|
||||
|
|
||||
{% if request.GET.c != 'podcast' %}
|
||||
<a href="?q={{ request.GET.q }}&c=podcast">播客</a>
|
||||
{% else %}
|
||||
播客
|
||||
{% endif %}
|
||||
{% endif %}
|
||||
|
|
||||
{% if request.GET.c != 'music' %}
|
||||
<a href="?q={{ request.GET.q }}&c=music">音乐</a>
|
||||
{% else %}
|
||||
音乐
|
||||
{% if 'music' in cats %}
|
||||
|
|
||||
{% if request.GET.c != 'music' %}
|
||||
<a href="?q={{ request.GET.q }}&c=music">音乐</a>
|
||||
{% else %}
|
||||
音乐
|
||||
{% endif %}
|
||||
{% endif %}
|
||||
|
|
||||
{% if request.GET.c != 'game' %}
|
||||
<a href="?q={{ request.GET.q }}&c=game">游戏</a>
|
||||
{% else %}
|
||||
游戏
|
||||
{% if 'game' in cats %}
|
||||
|
|
||||
{% if request.GET.c != 'game' %}
|
||||
<a href="?q={{ request.GET.q }}&c=game">游戏</a>
|
||||
{% else %}
|
||||
游戏
|
||||
{% endif %}
|
||||
{% endif %}
|
||||
|
|
||||
{% if request.GET.c != 'performance' %}
|
||||
<a href="?q={{ request.GET.q }}&c=performance">演出</a>
|
||||
{% else %}
|
||||
演出
|
||||
{% if 'performance' in cats %}
|
||||
|
|
||||
{% if request.GET.c != 'performance' %}
|
||||
<a href="?q={{ request.GET.q }}&c=performance">演出</a>
|
||||
{% else %}
|
||||
演出
|
||||
{% endif %}
|
||||
{% endif %}
|
||||
</div>
|
||||
</hgroup>
|
||||
|
@ -116,24 +130,24 @@
|
|||
</div>
|
||||
<div class="pagination">
|
||||
{% if pagination.has_prev %}
|
||||
<a href="?page=1&{% if request.GET.q %}q={{ request.GET.q }}{% elif request.GET.tag %}tag={{ request.GET.tag }}{% endif %}{% if request.GET.c %}&c={{ request.GET.c }}{% endif %}"
|
||||
<a href="?page=1&{% if request.GET.q %}q={{ request.GET.q }}{% elif request.GET.tag %}tag={{ request.GET.tag }}{% endif %}{% if request.GET.c %}&c={{ request.GET.c }}{% endif %}"
|
||||
class="pagination__nav-link pagination__nav-link">«</a>
|
||||
<a href="?page={{ pagination.previous_page }}&{% if request.GET.q %}q={{ request.GET.q }}{% elif request.GET.tag %}tag={{ request.GET.tag }}{% endif %}{% if request.GET.c %}&c={{ request.GET.c }}{% endif %}"
|
||||
<a href="?page={{ pagination.previous_page }}&{% if request.GET.q %}q={{ request.GET.q }}{% elif request.GET.tag %}tag={{ request.GET.tag }}{% endif %}{% if request.GET.c %}&c={{ request.GET.c }}{% endif %}"
|
||||
class="pagination__nav-link pagination__nav-link--right-margin pagination__nav-link">‹</a>
|
||||
{% endif %}
|
||||
{% for page in pagination.page_range %}
|
||||
{% if page == pagination.current_page %}
|
||||
<a href="?page={{ page }}&{% if request.GET.q %}q={{ request.GET.q }}{% elif request.GET.tag %}tag={{ request.GET.tag }}{% endif %}{% if request.GET.c %}&c={{ request.GET.c }}{% endif %}"
|
||||
<a href="?page={{ page }}&{% if request.GET.q %}q={{ request.GET.q }}{% elif request.GET.tag %}tag={{ request.GET.tag }}{% endif %}{% if request.GET.c %}&c={{ request.GET.c }}{% endif %}"
|
||||
class="pagination__page-link pagination__page-link--current">{{ page }}</a>
|
||||
{% else %}
|
||||
<a href="?page={{ page }}&{% if request.GET.q %}q={{ request.GET.q }}{% elif request.GET.tag %}tag={{ request.GET.tag }}{% endif %}{% if request.GET.c %}&c={{ request.GET.c }}{% endif %}"
|
||||
<a href="?page={{ page }}&{% if request.GET.q %}q={{ request.GET.q }}{% elif request.GET.tag %}tag={{ request.GET.tag }}{% endif %}{% if request.GET.c %}&c={{ request.GET.c }}{% endif %}"
|
||||
class="pagination__page-link">{{ page }}</a>
|
||||
{% endif %}
|
||||
{% endfor %}
|
||||
{% if pagination.has_next %}
|
||||
<a href="?page={{ pagination.next_page }}&{% if request.GET.q %}q={{ request.GET.q }}{% elif request.GET.tag %}tag={{ request.GET.tag }}{% endif %}{% if request.GET.c %}&c={{ request.GET.c }}{% endif %}"
|
||||
<a href="?page={{ pagination.next_page }}&{% if request.GET.q %}q={{ request.GET.q }}{% elif request.GET.tag %}tag={{ request.GET.tag }}{% endif %}{% if request.GET.c %}&c={{ request.GET.c }}{% endif %}"
|
||||
class="pagination__nav-link pagination__nav-link--left-margin">›</a>
|
||||
<a href="?page={{ pagination.last_page }}&{% if request.GET.q %}q={{ request.GET.q }}{% elif request.GET.tag %}tag={{ request.GET.tag }}{% endif %}{% if request.GET.c %}&c={{ request.GET.c }}{% endif %}"
|
||||
<a href="?page={{ pagination.last_page }}&{% if request.GET.q %}q={{ request.GET.q }}{% elif request.GET.tag %}tag={{ request.GET.tag }}{% endif %}{% if request.GET.c %}&c={{ request.GET.c }}{% endif %}"
|
||||
class="pagination__nav-link">»</a>
|
||||
{% endif %}
|
||||
</div>
|
||||
|
|
|
@ -1,4 +1,5 @@
|
|||
{% load admin_url %}
|
||||
{% load duration %}
|
||||
{% load static %}
|
||||
{% load i18n %}
|
||||
<header class="container-fluid">
|
||||
|
@ -18,19 +19,32 @@
|
|||
class="search"
|
||||
value="{{ request.GET.q|default:'' }}" />
|
||||
<select name="c">
|
||||
{% visible_categories as cats %}
|
||||
<option value="all">全部</option>
|
||||
<option {% if request.GET.c and request.GET.c == 'book' or '/book/' in request.path %}selected{% endif %}
|
||||
value="book">书籍</option>
|
||||
<option {% if request.GET.c and request.GET.c == 'movietv' or '/movie/' in request.path or '/tv/' in request.path %}selected{% endif %}
|
||||
value="movietv">影视</option>
|
||||
<option {% if request.GET.c and request.GET.c == 'podcast' or '/podcast/' in request.path %}selected{% endif %}
|
||||
value="podcast">播客</option>
|
||||
<option {% if request.GET.c and request.GET.c == 'music' or '/album/' in request.path %}selected{% endif %}
|
||||
value="music">音乐</option>
|
||||
<option {% if request.GET.c and request.GET.c == 'game' or '/game/' in request.path %}selected{% endif %}
|
||||
value="game">游戏</option>
|
||||
<option {% if request.GET.c and request.GET.c == 'performance' or '/performance/' in request.path %}selected{% endif %}
|
||||
value="performance">演出</option>
|
||||
{% if 'book' in cats %}
|
||||
<option {% if request.GET.c == 'book' or '/book/' in request.path %}selected{% endif %}
|
||||
value="book">书籍</option>
|
||||
{% endif %}
|
||||
{% if 'movie' in cats or 'tv' in cats %}
|
||||
<option {% if request.GET.c and request.GET.c == 'movietv' or '/movie/' in request.path or '/tv/' in request.path %}selected{% endif %}
|
||||
value="movietv">影视</option>
|
||||
{% endif %}
|
||||
{% if 'podcast' in cats %}
|
||||
<option {% if request.GET.c and request.GET.c == 'podcast' or '/podcast/' in request.path %}selected{% endif %}
|
||||
value="podcast">播客</option>
|
||||
{% endif %}
|
||||
{% if 'music' in cats %}
|
||||
<option {% if request.GET.c and request.GET.c == 'music' or '/album/' in request.path %}selected{% endif %}
|
||||
value="music">音乐</option>
|
||||
{% endif %}
|
||||
{% if 'game' in cats %}
|
||||
<option {% if request.GET.c and request.GET.c == 'game' or '/game/' in request.path %}selected{% endif %}
|
||||
value="game">游戏</option>
|
||||
{% endif %}
|
||||
{% if 'performance' in cats %}
|
||||
<option {% if request.GET.c == 'performance' or '/performance/' in request.path %}selected{% endif %}
|
||||
value="performance">演出</option>
|
||||
{% endif %}
|
||||
</select>
|
||||
<input type="submit" value="" class="fa-solid" />
|
||||
</form>
|
||||
|
|
|
@ -2,10 +2,22 @@ from django import template
|
|||
from django.template.defaultfilters import stringfilter
|
||||
from django.utils.text import Truncator
|
||||
from django.utils.safestring import mark_safe
|
||||
from catalog.common.models import ItemCategory, item_categories
|
||||
from catalog.search.views import visible_categories as _visible_categories
|
||||
|
||||
register = template.Library()
|
||||
|
||||
|
||||
@register.simple_tag(takes_context=True)
|
||||
def visible_categories(context):
|
||||
return _visible_categories(context["request"])
|
||||
|
||||
|
||||
@register.simple_tag
|
||||
def all_categories():
|
||||
return item_categories()
|
||||
|
||||
|
||||
@register.filter(is_safe=True)
|
||||
@stringfilter
|
||||
def duration_format(value, unit):
|
||||
|
|
|
@ -88,7 +88,7 @@ classDiagram
|
|||
|
||||
Add a new site
|
||||
--------------
|
||||
> **Site official API** should be the prioritised way to get data when adding a new site.
|
||||
- If official API is available for the site, it should be the preferred way to get data.
|
||||
- add a new value to `IdType` and `SiteName` in `catalog/common/models.py`
|
||||
- add a new file in `catalog/sites/`, a new class inherits `AbstractSite`, with:
|
||||
* `SITE_NAME`
|
||||
|
|
|
@ -457,9 +457,16 @@ def swap_login(request, token, site, refresh_token):
|
|||
return redirect(reverse("users:data"))
|
||||
|
||||
|
||||
def clear_preference_cache(request):
|
||||
for key in list(request.session.keys()):
|
||||
if key.startswith("p_"):
|
||||
del request.session[key]
|
||||
|
||||
|
||||
def auth_login(request, user):
|
||||
"""Decorates django ``login()``. Attach token to session."""
|
||||
auth.login(request, user, backend="mastodon.auth.OAuth2Backend")
|
||||
clear_preference_cache(request)
|
||||
if (
|
||||
user.mastodon_last_refresh < timezone.now() - timedelta(hours=1)
|
||||
or user.mastodon_account == {}
|
||||
|
|
|
@ -29,6 +29,7 @@ def preferences(request):
|
|||
preference.default_no_share = bool(request.POST.get("default_no_share"))
|
||||
preference.no_anonymous_view = bool(request.POST.get("no_anonymous_view"))
|
||||
preference.classic_homepage = int(request.POST.get("classic_homepage"))
|
||||
preference.hidden_categories = request.POST.getlist("hidden_categories")
|
||||
preference.mastodon_publish_public = bool(
|
||||
request.POST.get("mastodon_publish_public")
|
||||
)
|
||||
|
@ -45,8 +46,10 @@ def preferences(request):
|
|||
"mastodon_publish_public",
|
||||
"mastodon_append_tag",
|
||||
"show_last_edit",
|
||||
"hidden_categories",
|
||||
]
|
||||
)
|
||||
clear_preference_cache(request)
|
||||
return render(request, "users/preferences.html")
|
||||
|
||||
|
||||
|
|
|
@ -20,7 +20,7 @@ from django.templatetags.static import static
|
|||
import hashlib
|
||||
from loguru import logger
|
||||
|
||||
RESERVED_USERNAMES = [
|
||||
_RESERVED_USERNAMES = [
|
||||
"connect",
|
||||
"oauth2_login",
|
||||
"__",
|
||||
|
@ -40,7 +40,7 @@ class UsernameValidator(validators.RegexValidator):
|
|||
flags = re.ASCII
|
||||
|
||||
def __call__(self, value):
|
||||
if value and value.lower() in RESERVED_USERNAMES:
|
||||
if value and value.lower() in _RESERVED_USERNAMES:
|
||||
raise ValidationError(self.message, code=self.code)
|
||||
return super().__call__(value)
|
||||
|
||||
|
@ -52,6 +52,7 @@ def report_image_path(instance, filename):
|
|||
|
||||
|
||||
class User(AbstractUser):
|
||||
preference: "Preference"
|
||||
username_validator = UsernameValidator()
|
||||
username = models.CharField(
|
||||
_("username"),
|
||||
|
@ -457,7 +458,7 @@ class User(AbstractUser):
|
|||
or target.mastodon_site in self.mastodon_domain_blocks
|
||||
)
|
||||
if target.is_authenticated
|
||||
else self.preference.no_anonymous_view # type: ignore
|
||||
else self.preference.no_anonymous_view
|
||||
)
|
||||
|
||||
def is_blocked_by(self, target):
|
||||
|
@ -556,6 +557,7 @@ class Preference(models.Model):
|
|||
mastodon_append_tag = models.CharField(max_length=2048, default="")
|
||||
show_last_edit = models.PositiveSmallIntegerField(default=0)
|
||||
no_anonymous_view = models.PositiveSmallIntegerField(default=0)
|
||||
hidden_categories = models.JSONField(default=list)
|
||||
|
||||
def __str__(self):
|
||||
return str(self.user)
|
||||
|
|
|
@ -4,6 +4,7 @@
|
|||
{% load mastodon %}
|
||||
{% load oauth_token %}
|
||||
{% load truncate %}
|
||||
{% load duration %}
|
||||
{% load thumb %}
|
||||
<!DOCTYPE html>
|
||||
<html lang="zh" class="classic-page">
|
||||
|
@ -108,6 +109,18 @@
|
|||
placeholder="例如 #我的书影音"
|
||||
value="{{ request.user.preference.mastodon_append_tag }}">
|
||||
</fieldset>
|
||||
<fieldset>
|
||||
<legend>{% trans '搜索时不显示以下类型:' %}</legend>
|
||||
<select name="hidden_categories" size="3" multiple>
|
||||
{% all_categories as categories %}
|
||||
{% for c in categories %}
|
||||
<option value="{{ c.value }}"
|
||||
{% if c in request.user.preference.hidden_categories %}selected{% endif %}>
|
||||
{{ c.label }}
|
||||
</option>
|
||||
{% endfor %}
|
||||
</select>
|
||||
</fieldset>
|
||||
<input type="submit" value="{% trans '保存' %}">
|
||||
</form>
|
||||
</details>
|
||||
|
|
Loading…
Add table
Reference in a new issue