feat(sessions): honor ?sort= on list_sessions + eager-load relations (#68)

This commit is contained in:
2026-06-21 13:58:33 +02:00
parent fb985fac9e
commit 5fc9cc03da
2 changed files with 36 additions and 1 deletions
+12 -1
View File
@@ -1,5 +1,6 @@
from typing import Any, TypedDict from typing import Any, TypedDict
from django.contrib import messages
from django.contrib.auth.decorators import login_required from django.contrib.auth.decorators import login_required
from django.db.models import Q from django.db.models import Q
from django.http import HttpRequest, HttpResponse from django.http import HttpRequest, HttpResponse
@@ -38,6 +39,12 @@ from common.time import (
from common.utils import paginate, truncate from common.utils import paginate, truncate
from games.forms import SessionForm from games.forms import SessionForm
from games.models import Device, Game, Session from games.models import Device, Game, Session
from games.sorting import (
SESSION_DEFAULT_SORT,
SESSION_SORTS,
apply_sort,
parse_find_filter,
)
class SessionRowData(TypedDict): class SessionRowData(TypedDict):
@@ -119,7 +126,7 @@ def session_row(session: Session, device_list, csrf_token: str) -> Node:
@login_required @login_required
def list_sessions(request: HttpRequest, search_string: str = "") -> HttpResponse: def list_sessions(request: HttpRequest, search_string: str = "") -> HttpResponse:
sessions = Session.objects.order_by("-timestamp_start", "created_at") sessions = Session.objects.select_related("game", "game__platform", "device")
device_list = Device.objects.order_by("name") device_list = Device.objects.order_by("name")
# ── Structured filter (JSON) ── # ── Structured filter (JSON) ──
@@ -145,6 +152,10 @@ def list_sessions(request: HttpRequest, search_string: str = "") -> HttpResponse
last_session = sessions.latest() last_session = sessions.latest()
except Session.DoesNotExist: except Session.DoesNotExist:
last_session = None last_session = None
sort = apply_sort(sessions, parse_find_filter(request), SESSION_SORTS, SESSION_DEFAULT_SORT)
sessions = sort.queryset
for key in sort.unknown:
messages.warning(request, f"Unknown sort field '{key}' was ignored.")
sessions, page_obj, elided_page_range = paginate(request, sessions) sessions, page_obj, elided_page_range = paginate(request, sessions)
csrf_token = get_token(request) csrf_token = get_token(request)
+24
View File
@@ -170,3 +170,27 @@ class TestListGamesSort:
response = logged_client.get(reverse("games:list_games"), {"sort": "name"}) response = logged_client.get(reverse("games:list_games"), {"sort": "name"})
warnings = [str(m) for m in get_messages(response.wsgi_request)] warnings = [str(m) for m in get_messages(response.wsgi_request)]
assert warnings == [] assert warnings == []
class TestListSessionsSort:
def test_sort_by_duration_descending(self, logged_client, two_games):
alpha, beta = two_games
Session.objects.create(
game=alpha,
timestamp_start=datetime(2022, 1, 1, 10, tzinfo=ZONEINFO),
timestamp_end=datetime(2022, 1, 1, 10, 30, tzinfo=ZONEINFO), # 30 min
)
Session.objects.create(
game=beta,
timestamp_start=datetime(2022, 1, 2, 10, tzinfo=ZONEINFO),
timestamp_end=datetime(2022, 1, 2, 13, tzinfo=ZONEINFO), # 3 h
)
response = logged_client.get(reverse("games:list_sessions"), {"sort": "-duration"})
assert response.status_code == 200
body = response.content.decode()
assert body.index("Beta") < body.index("Alpha") # longer session first
def test_unknown_sort_emits_warning(self, logged_client, two_games):
response = logged_client.get(reverse("games:list_sessions"), {"sort": "nope"})
warnings = [str(m) for m in get_messages(response.wsgi_request)]
assert any("nope" in w for w in warnings)