From 5fc9cc03da61062c0f13082e4d446a6c59915f82 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Luk=C3=A1=C5=A1=20Kucharczyk?= Date: Sun, 21 Jun 2026 13:58:33 +0200 Subject: [PATCH] feat(sessions): honor ?sort= on list_sessions + eager-load relations (#68) --- games/views/session.py | 13 ++++++++++++- tests/test_sorting.py | 24 ++++++++++++++++++++++++ 2 files changed, 36 insertions(+), 1 deletion(-) diff --git a/games/views/session.py b/games/views/session.py index a52c537..aeff163 100644 --- a/games/views/session.py +++ b/games/views/session.py @@ -1,5 +1,6 @@ from typing import Any, TypedDict +from django.contrib import messages from django.contrib.auth.decorators import login_required from django.db.models import Q from django.http import HttpRequest, HttpResponse @@ -38,6 +39,12 @@ from common.time import ( from common.utils import paginate, truncate from games.forms import SessionForm from games.models import Device, Game, Session +from games.sorting import ( + SESSION_DEFAULT_SORT, + SESSION_SORTS, + apply_sort, + parse_find_filter, +) class SessionRowData(TypedDict): @@ -119,7 +126,7 @@ def session_row(session: Session, device_list, csrf_token: str) -> Node: @login_required 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") # ── Structured filter (JSON) ── @@ -145,6 +152,10 @@ def list_sessions(request: HttpRequest, search_string: str = "") -> HttpResponse last_session = sessions.latest() except Session.DoesNotExist: 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) csrf_token = get_token(request) diff --git a/tests/test_sorting.py b/tests/test_sorting.py index 268444d..255093d 100644 --- a/tests/test_sorting.py +++ b/tests/test_sorting.py @@ -170,3 +170,27 @@ class TestListGamesSort: response = logged_client.get(reverse("games:list_games"), {"sort": "name"}) warnings = [str(m) for m in get_messages(response.wsgi_request)] 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)