sort marks by rating
This commit is contained in:
parent
1e3e409a7e
commit
c7445f8f61
15 changed files with 170 additions and 168 deletions
|
@ -141,7 +141,7 @@ def search(request):
|
|||
{
|
||||
"items": items,
|
||||
"dup_items": dup_items,
|
||||
"pagination": PageLinksGenerator(PAGE_LINK_NUMBER, p, num_pages),
|
||||
"pagination": PageLinksGenerator(p, num_pages, request.GET),
|
||||
"sites": SiteName.labels,
|
||||
"hide_category": hide_category,
|
||||
},
|
||||
|
|
|
@ -157,7 +157,7 @@ def mark_list(request, item_path, item_uuid, following_only=False):
|
|||
paginator = Paginator(queryset, NUM_REVIEWS_ON_LIST_PAGE)
|
||||
page_number = request.GET.get("page", default=1)
|
||||
marks = paginator.get_page(page_number)
|
||||
pagination = PageLinksGenerator(PAGE_LINK_NUMBER, page_number, paginator.num_pages)
|
||||
pagination = PageLinksGenerator(page_number, paginator.num_pages, request.GET)
|
||||
return render(
|
||||
request,
|
||||
"item_mark_list.html",
|
||||
|
@ -179,7 +179,7 @@ def review_list(request, item_path, item_uuid):
|
|||
paginator = Paginator(queryset, NUM_REVIEWS_ON_LIST_PAGE)
|
||||
page_number = request.GET.get("page", default=1)
|
||||
reviews = paginator.get_page(page_number)
|
||||
pagination = PageLinksGenerator(PAGE_LINK_NUMBER, page_number, paginator.num_pages)
|
||||
pagination = PageLinksGenerator(page_number, paginator.num_pages, request.GET)
|
||||
return render(
|
||||
request,
|
||||
"item_review_list.html",
|
||||
|
|
|
@ -184,3 +184,26 @@ form img {
|
|||
margin: 0 0.5em;
|
||||
}
|
||||
}
|
||||
|
||||
.pagination {
|
||||
text-align: center;
|
||||
width: 100%;
|
||||
margin-top: 1em;
|
||||
a {
|
||||
margin: 0 0.3em;
|
||||
}
|
||||
.s, .prev, .next {
|
||||
font-size: 1.4em;
|
||||
}
|
||||
.prev {
|
||||
margin-right: 0.5em;
|
||||
}
|
||||
.next {
|
||||
margin-left: 0.5em;
|
||||
}
|
||||
.current {
|
||||
font-weight: bold;
|
||||
font-size: 1.1em;
|
||||
color: var(--pico-secondary);
|
||||
}
|
||||
}
|
||||
|
|
|
@ -954,57 +954,10 @@ $panel-padding : 0
|
|||
padding: 0
|
||||
list-style: none
|
||||
|
||||
.pagination
|
||||
// position: absolute
|
||||
// bottom: 30px
|
||||
// left: 50%
|
||||
// transform: translateX(-50%)
|
||||
text-align: center
|
||||
width: 100%
|
||||
|
||||
& &__page-link
|
||||
font-weight: normal
|
||||
margin: 0 5px
|
||||
|
||||
&--current
|
||||
font-weight: bold
|
||||
font-size: 1.2em
|
||||
// text-decoration: underline
|
||||
color: $color-secondary
|
||||
|
||||
& &__nav-link
|
||||
font-size: 1.4em
|
||||
margin: 0 2px
|
||||
|
||||
$nav-link-edge-margin-width: 18px
|
||||
|
||||
&--right-margin
|
||||
margin-right: $nav-link-edge-margin-width
|
||||
|
||||
&--left-margin
|
||||
margin-left: $nav-link-edge-margin-width
|
||||
|
||||
&--hidden
|
||||
display: none
|
||||
|
||||
|
||||
// Small devices (landscape phones, 576px and up)
|
||||
@media (max-width: $small-devices)
|
||||
.pagination
|
||||
& &__page-link
|
||||
margin: 0 3px
|
||||
|
||||
& &__nav-link
|
||||
font-size: 1.4em
|
||||
margin: 0 2px
|
||||
|
||||
$nav-link-edge-margin-width: 10px
|
||||
|
||||
&--right-margin
|
||||
margin-right: $nav-link-edge-margin-width
|
||||
|
||||
&--left-margin
|
||||
margin-left: $nav-link-edge-margin-width
|
||||
pass
|
||||
// Medium devices (tablets, 768px and up)
|
||||
@media (max-width: $medium-devices)
|
||||
pass
|
||||
|
|
|
@ -1,23 +1,20 @@
|
|||
<div class="pagination">
|
||||
{% if pagination.has_prev %}
|
||||
<a href="?{% if request.GET.t %}t={{ request.GET.t }}&{% endif %}page=1"
|
||||
class="pagination__nav-link pagination__nav-link">«</a>
|
||||
<a href="?{% if request.GET.t %}t={{ request.GET.t }}&{% endif %}page={{ pagination.previous_page }}"
|
||||
class="pagination__nav-link pagination__nav-link--right-margin pagination__nav-link">‹</a>
|
||||
<a href="?{{ pagination.query_string }}page=1" class="s">«</a>
|
||||
<a href="?{{ pagination.query_string }}page={{ pagination.previous_page }}"
|
||||
class="prev">‹</a>
|
||||
{% endif %}
|
||||
{% for page in pagination.page_range %}
|
||||
{% if page == pagination.current_page %}
|
||||
<a href="?{% if request.GET.t %}t={{ request.GET.t }}&{% endif %}page={{ page }}"
|
||||
class="pagination__page-link pagination__page-link--current">{{ page }}</a>
|
||||
<a href="?{{ pagination.query_string }}page={{ page }}" class="current">{{ page }}</a>
|
||||
{% else %}
|
||||
<a href="?{% if request.GET.t %}t={{ request.GET.t }}&{% endif %}page={{ page }}"
|
||||
class="pagination__page-link">{{ page }}</a>
|
||||
<a href="?{{ pagination.query_string }}page={{ page }}">{{ page }}</a>
|
||||
{% endif %}
|
||||
{% endfor %}
|
||||
{% if pagination.has_next %}
|
||||
<a href="?{% if request.GET.t %}t={{ request.GET.t }}&{% endif %}page={{ pagination.next_page }}"
|
||||
class="pagination__nav-link pagination__nav-link--left-margin">›</a>
|
||||
<a href="?{% if request.GET.t %}t={{ request.GET.t }}&{% endif %}page={{ pagination.last_page }}"
|
||||
class="pagination__nav-link">»</a>
|
||||
<a href="?{{ pagination.query_string }}page={{ pagination.next_page }}"
|
||||
class="next">›</a>
|
||||
<a href="?{{ pagination.query_string }}page={{ pagination.last_page }}"
|
||||
class="s">»</a>
|
||||
{% endif %}
|
||||
</div>
|
||||
|
|
|
@ -1,11 +1,15 @@
|
|||
import functools
|
||||
import re
|
||||
import uuid
|
||||
from typing import TYPE_CHECKING
|
||||
|
||||
from django.http import Http404, HttpRequest, HttpResponseRedirect
|
||||
from django.db.models import query
|
||||
from django.http import Http404, HttpRequest, HttpResponseRedirect, QueryDict
|
||||
from django.utils import timezone
|
||||
from django.utils.baseconv import base62
|
||||
|
||||
from .config import PAGE_LINK_NUMBER
|
||||
|
||||
if TYPE_CHECKING:
|
||||
from users.models import APIdentity, User
|
||||
|
||||
|
@ -82,8 +86,19 @@ class PageLinksGenerator:
|
|||
length -- the number of page links in pagination
|
||||
"""
|
||||
|
||||
def __init__(self, length: int, current_page: int, total_pages: int):
|
||||
def __init__(
|
||||
self, current_page: int, total_pages: int, query: QueryDict | None = None
|
||||
):
|
||||
length = PAGE_LINK_NUMBER
|
||||
current_page = int(current_page)
|
||||
self.query_string = ""
|
||||
if query:
|
||||
q = query.copy()
|
||||
if q.get("page"):
|
||||
q.pop("page")
|
||||
self.query_string = q.urlencode()
|
||||
if self.query_string:
|
||||
self.query_string += "&"
|
||||
self.current_page = current_page
|
||||
self.previous_page = current_page - 1 if current_page > 1 else None
|
||||
self.next_page = current_page + 1 if current_page < total_pages else None
|
||||
|
|
|
@ -1,11 +1,12 @@
|
|||
from datetime import datetime
|
||||
from typing import Any
|
||||
|
||||
from django.core.validators import MaxValueValidator, MinValueValidator, RegexValidator
|
||||
from django.db import connection, models
|
||||
from django.core.validators import MaxValueValidator, MinValueValidator
|
||||
from django.db import models
|
||||
from django.db.models import Avg, Count, Q
|
||||
from django.utils.translation import gettext_lazy as _
|
||||
|
||||
from catalog.models import Item, ItemCategory
|
||||
from catalog.models import Item
|
||||
from users.models import APIdentity
|
||||
|
||||
from .common import Content
|
||||
|
@ -116,24 +117,22 @@ class Rating(Content):
|
|||
|
||||
@staticmethod
|
||||
def update_item_rating(
|
||||
item: Item, owner: APIdentity, rating_grade: int | None, visibility: int = 0
|
||||
item: Item,
|
||||
owner: APIdentity,
|
||||
rating_grade: int | None,
|
||||
visibility: int = 0,
|
||||
created_time: datetime | None = None,
|
||||
):
|
||||
if rating_grade and (rating_grade < 1 or rating_grade > 10):
|
||||
raise ValueError(f"Invalid rating grade: {rating_grade}")
|
||||
rating = Rating.objects.filter(owner=owner, item=item).first()
|
||||
if not rating_grade:
|
||||
if rating:
|
||||
rating.delete()
|
||||
rating = None
|
||||
elif rating is None:
|
||||
rating = Rating.objects.create(
|
||||
owner=owner, item=item, grade=rating_grade, visibility=visibility
|
||||
)
|
||||
elif rating.grade != rating_grade or rating.visibility != visibility:
|
||||
rating.visibility = visibility
|
||||
rating.grade = rating_grade
|
||||
rating.save()
|
||||
return rating
|
||||
Rating.objects.filter(owner=owner, item=item).delete()
|
||||
else:
|
||||
d: dict[str, Any] = {"grade": rating_grade, "visibility": visibility}
|
||||
if created_time:
|
||||
d["created_time"] = created_time
|
||||
r, _ = Rating.objects.update_or_create(owner=owner, item=item, defaults=d)
|
||||
return r
|
||||
|
||||
@staticmethod
|
||||
def get_item_rating(item: Item, owner: APIdentity) -> int | None:
|
||||
|
|
|
@ -259,6 +259,15 @@ class ShelfManager:
|
|||
else:
|
||||
return qs
|
||||
|
||||
def get_members(
|
||||
self, shelf_type: ShelfType, item_category: ItemCategory | None = None
|
||||
):
|
||||
qs = self.shelf_list[shelf_type].members.all()
|
||||
if item_category:
|
||||
return qs.filter(q_item_in_category(item_category))
|
||||
else:
|
||||
return qs
|
||||
|
||||
# def get_items_on_shelf(self, item_category, shelf_type):
|
||||
# shelf = (
|
||||
# self.owner.shelf_set.all()
|
||||
|
|
|
@ -5,17 +5,18 @@
|
|||
<section class="filter">
|
||||
<script>
|
||||
function filter() {
|
||||
location = '{{ user.identity.url }}' + $('#shelf')[0].value + '/' + $('#category')[0].value + '/' + ( $('#year')[0].value ? $('#year')[0].value + '/' : '' );
|
||||
var q = [];
|
||||
if ($('#year')[0].value) {
|
||||
q.push('year=' + $('#year')[0].value)
|
||||
}
|
||||
if ($('#sort')[0].value) {
|
||||
q.push('sort=' + $('#sort')[0].value)
|
||||
}
|
||||
location = '{{ user.identity.url }}' + $('#shelf')[0].value + '/' + $('#category')[0].value + '/' + (q.length ? '?' + q.join('&') : '')
|
||||
}
|
||||
|
||||
</script>
|
||||
<form role="search" method="get" action="">
|
||||
<select name="year" id="year" onchange="filter()">
|
||||
<option value="">全部</option>
|
||||
{% for y in years %}
|
||||
<option value="{{ y }}" {% if y == year %}selected{% endif %}>{{ y }}</option>
|
||||
{% endfor %}
|
||||
</select>
|
||||
<form method="get" action="">
|
||||
<fieldset role="search" style="width: 100%; padding;">
|
||||
<select name="shelf" id="shelf" onchange="filter()">
|
||||
{% for typ, label in shelf_labels %}
|
||||
{% if label %}
|
||||
|
@ -51,6 +52,18 @@
|
|||
value="performance">演出</option>
|
||||
{% endif %}
|
||||
</select>
|
||||
<select name="year" id="year" onchange="filter()">
|
||||
<option value="">全部</option>
|
||||
{% for y in years %}
|
||||
<option value="{{ y }}" {% if y == year %}selected{% endif %}>{{ y }}</option>
|
||||
{% endfor %}
|
||||
</select>
|
||||
<select name="sort" id="sort" onchange="filter()">
|
||||
<option value="" disabled>排列顺序</option>
|
||||
<option value="">时间</option>
|
||||
<option value="rating" {% if 'rating' == sort %}selected{% endif %}>评分</option>
|
||||
</select>
|
||||
</fieldset>
|
||||
</form>
|
||||
<!--
|
||||
<style type="text/css">
|
||||
|
|
|
@ -101,15 +101,6 @@ urlpatterns = [
|
|||
user_mark_list,
|
||||
name="user_mark_list",
|
||||
),
|
||||
re_path(
|
||||
r"^users/(?P<user_name>[~A-Za-z0-9_\-.@]+)/(?P<shelf_type>"
|
||||
+ _get_all_shelf_types()
|
||||
+ ")/(?P<item_category>"
|
||||
+ _get_all_categories()
|
||||
+ r")/(?P<year>\d+)/$",
|
||||
user_mark_list,
|
||||
name="user_mark_list_year",
|
||||
),
|
||||
re_path(
|
||||
r"^users/(?P<user_name>[~A-Za-z0-9_\-.@]+)/reviews/(?P<item_category>"
|
||||
+ _get_all_categories()
|
||||
|
@ -117,13 +108,6 @@ urlpatterns = [
|
|||
user_review_list,
|
||||
name="user_review_list",
|
||||
),
|
||||
re_path(
|
||||
r"^users/(?P<user_name>[~A-Za-z0-9_\-.@]+)/reviews/(?P<item_category>"
|
||||
+ _get_all_categories()
|
||||
+ r")/(?P<year>\d+)/$",
|
||||
user_review_list,
|
||||
name="user_review_list_year",
|
||||
),
|
||||
re_path(
|
||||
r"^users/(?P<user_name>[~A-Za-z0-9_\-.@]+)/tags/(?P<tag_title>.+)/$",
|
||||
user_tag_member_list,
|
||||
|
|
|
@ -3,7 +3,7 @@ import datetime
|
|||
from django.contrib.auth.decorators import login_required
|
||||
from django.core.exceptions import BadRequest, ObjectDoesNotExist, PermissionDenied
|
||||
from django.core.paginator import Paginator
|
||||
from django.db.models import Min
|
||||
from django.db.models import F, Min, OuterRef, Subquery
|
||||
from django.shortcuts import get_object_or_404, redirect, render
|
||||
from django.urls import reverse
|
||||
from django.utils.translation import gettext_lazy as _
|
||||
|
@ -57,12 +57,15 @@ def render_list(
|
|||
item_category=None,
|
||||
tag_title=None,
|
||||
year=None,
|
||||
sort="time",
|
||||
):
|
||||
target = request.target_identity
|
||||
viewer = request.user.identity
|
||||
tag = None
|
||||
sort = request.GET.get("sort")
|
||||
year = request.GET.get("year")
|
||||
if type == "mark" and shelf_type:
|
||||
queryset = target.shelf_manager.get_latest_members(shelf_type, item_category)
|
||||
queryset = target.shelf_manager.get_members(shelf_type, item_category)
|
||||
elif type == "tagmember":
|
||||
tag = Tag.objects.filter(owner=target, title=tag_title).first()
|
||||
if not tag:
|
||||
|
@ -74,6 +77,15 @@ def render_list(
|
|||
queryset = Review.objects.filter(q_item_in_category(item_category))
|
||||
else:
|
||||
raise BadRequest()
|
||||
if sort == "rating":
|
||||
rating = Rating.objects.filter(
|
||||
owner_id=OuterRef("owner_id"), item_id=OuterRef("item_id")
|
||||
)
|
||||
queryset = queryset.alias(
|
||||
rating_grade=Subquery(rating.values("grade"))
|
||||
).order_by(F("rating_grade").desc(nulls_last=True))
|
||||
else:
|
||||
queryset = queryset.order_by("-created_time")
|
||||
start_date = queryset.aggregate(Min("created_time"))["created_time__min"]
|
||||
if start_date:
|
||||
start_year = start_date.year
|
||||
|
@ -81,16 +93,14 @@ def render_list(
|
|||
years = reversed(range(start_year, current_year + 1))
|
||||
else:
|
||||
years = []
|
||||
queryset = queryset.filter(
|
||||
q_owned_piece_visible_to_user(request.user, target)
|
||||
).order_by("-created_time")
|
||||
queryset = queryset.filter(q_owned_piece_visible_to_user(request.user, target))
|
||||
if year:
|
||||
year = int(year)
|
||||
queryset = queryset.filter(created_time__year=year)
|
||||
paginator = Paginator(queryset, PAGE_SIZE)
|
||||
page_number = int(request.GET.get("page", default=1))
|
||||
members = paginator.get_page(page_number)
|
||||
pagination = PageLinksGenerator(PAGE_SIZE, page_number, paginator.num_pages)
|
||||
pagination = PageLinksGenerator(page_number, paginator.num_pages, request.GET)
|
||||
shelf_labels = get_shelf_labels_for_category(item_category) if item_category else []
|
||||
return render(
|
||||
request,
|
||||
|
@ -103,6 +113,7 @@ def render_list(
|
|||
"pagination": pagination,
|
||||
"years": years,
|
||||
"year": year,
|
||||
"sort": sort,
|
||||
"shelf": shelf_type,
|
||||
"shelf_labels": shelf_labels,
|
||||
"category": item_category,
|
||||
|
|
|
@ -11,7 +11,7 @@ from django.utils.dateparse import parse_datetime
|
|||
from django.utils.translation import gettext_lazy as _
|
||||
|
||||
from catalog.models import *
|
||||
from common.utils import AuthedHttpRequest, PageLinksGenerator, get_uuid_or_404
|
||||
from common.utils import AuthedHttpRequest, get_uuid_or_404
|
||||
from mastodon.api import boost_toot_later, share_comment
|
||||
from takahe.utils import Takahe
|
||||
|
||||
|
@ -184,14 +184,7 @@ def comment(request: AuthedHttpRequest, item_uuid):
|
|||
raise BadRequest()
|
||||
|
||||
|
||||
def user_mark_list(
|
||||
request: AuthedHttpRequest, user_name, shelf_type, item_category, year=None
|
||||
):
|
||||
def user_mark_list(request: AuthedHttpRequest, user_name, shelf_type, item_category):
|
||||
return render_list(
|
||||
request,
|
||||
user_name,
|
||||
"mark",
|
||||
shelf_type=shelf_type,
|
||||
item_category=item_category,
|
||||
year=year,
|
||||
request, user_name, "mark", shelf_type=shelf_type, item_category=item_category
|
||||
)
|
||||
|
|
|
@ -5,12 +5,7 @@ from django.urls import reverse
|
|||
from django.utils.translation import gettext_lazy as _
|
||||
from django.views.decorators.http import require_http_methods
|
||||
|
||||
from common.utils import (
|
||||
AuthedHttpRequest,
|
||||
PageLinksGenerator,
|
||||
get_uuid_or_404,
|
||||
target_identity_required,
|
||||
)
|
||||
from common.utils import AuthedHttpRequest, get_uuid_or_404, target_identity_required
|
||||
from mastodon.api import boost_toot_later
|
||||
from takahe.utils import Takahe
|
||||
|
||||
|
|
|
@ -13,7 +13,7 @@ from django.utils.translation import gettext_lazy as _
|
|||
from django.views.decorators.http import require_http_methods
|
||||
|
||||
from catalog.models import *
|
||||
from common.utils import AuthedHttpRequest, PageLinksGenerator, get_uuid_or_404
|
||||
from common.utils import AuthedHttpRequest, get_uuid_or_404
|
||||
from journal.models.renderers import convert_leading_space_in_md, render_md
|
||||
from users.models.apidentity import APIdentity
|
||||
|
||||
|
@ -97,10 +97,8 @@ def review_edit(request: AuthedHttpRequest, item_uuid, review_uuid=None):
|
|||
raise BadRequest()
|
||||
|
||||
|
||||
def user_review_list(request, user_name, item_category, year=None):
|
||||
return render_list(
|
||||
request, user_name, "review", item_category=item_category, year=None
|
||||
)
|
||||
def user_review_list(request, user_name, item_category):
|
||||
return render_list(request, user_name, "review", item_category=item_category)
|
||||
|
||||
|
||||
MAX_ITEM_PER_TYPE = 10
|
||||
|
|
|
@ -1,2 +1,14 @@
|
|||
User-agent: CCBot
|
||||
Disallow: /review/
|
||||
|
||||
User-agent: GPTBot
|
||||
Disallow: /review/
|
||||
|
||||
User-agent: Google-Extended
|
||||
Disallow: /review/
|
||||
|
||||
User-agent: FacebookBot
|
||||
Disallow: /review/
|
||||
|
||||
User-agent: Omgilibot
|
||||
Disallow: /review/
|
||||
|
|
Loading…
Add table
Reference in a new issue