From 6bb8904f65260d69b0382b6dea55ec52a16ba704 Mon Sep 17 00:00:00 2001 From: Daniel Schadt Date: Tue, 9 May 2023 19:54:21 +0200 Subject: implement sorting for the browse page --- fietsboek/templates/browse.jinja2 | 10 ++++++++ fietsboek/views/browse.py | 53 +++++++++++++++++++++++++++++++++++++-- 2 files changed, 61 insertions(+), 2 deletions(-) diff --git a/fietsboek/templates/browse.jinja2 b/fietsboek/templates/browse.jinja2 index f8937bb..9860201 100644 --- a/fietsboek/templates/browse.jinja2 +++ b/fietsboek/templates/browse.jinja2 @@ -105,6 +105,16 @@ {{ _("page.browse.filters.expand_advanced") }} +
+ +
diff --git a/fietsboek/views/browse.py b/fietsboek/views/browse.py index aed7d0a..1e97187 100644 --- a/fietsboek/views/browse.py +++ b/fietsboek/views/browse.py @@ -3,6 +3,7 @@ import datetime from collections.abc import Callable, Iterable from enum import Enum from io import RawIOBase +from typing import TypeVar from zipfile import ZIP_DEFLATED, ZipFile from pyramid.httpexceptions import HTTPBadRequest, HTTPForbidden, HTTPNotFound @@ -17,6 +18,8 @@ from sqlalchemy.sql import Selectable from .. import models, util from ..models.track import TrackType, TrackWithMetadata +T = TypeVar("T", bound=Enum) + class Stream(RawIOBase): """A :class:`Stream` represents an in-memory buffered FIFO. @@ -57,13 +60,25 @@ def _get_date(request: Request, name: str) -> datetime.date: raise HTTPBadRequest(f"Invalid date in {name!r}") from exc -def _get_enum(enum: type[Enum], value: str) -> Enum: +def _get_enum(enum: type[T], value: str) -> T: try: return enum[value] except KeyError as exc: raise HTTPBadRequest(f"Invalid enum value {value!r}") from exc +class ResultOrder(Enum): + """Enum representing the different ways in which the tracks can be sorted + in the result.""" + + DATE_ASC = "date-asc" + DATE_DESC = "date-desc" + LENGTH_ASC = "length-asc" + LENGTH_DESC = "length-desc" + DURATION_ASC = "duration-asc" + DURATION_DESC = "duration-desc" + + class Filter: """A class representing a filter that the user can apply to the track list.""" @@ -342,6 +357,36 @@ class FilterCollection(Filter): return cls(filters) +def apply_order(query: Selectable, track: AliasedClass, order: ResultOrder) -> Selectable: + """Applies a ``ORDER BY`` clause to the query. + + :raises ValueError: If the given order does not exist. + :param query: The query to adjust. + :param track: The aliased track class. + :param order: The order, one of the values given above. + :return: The modified query with the ORDER BY clause applied. + """ + if order == ResultOrder.DATE_DESC: + query = query.order_by(track.date_raw.desc()) + elif order == ResultOrder.DATE_ASC: + query = query.order_by(track.date_raw.asc()) + # Thanks to SQLAlchemy, the join for the ORDER BY query is done + # automatically. + elif order == ResultOrder.LENGTH_DESC: + query = query.order_by(models.TrackCache.length.desc()) + elif order == ResultOrder.LENGTH_ASC: + query = query.order_by(models.TrackCache.length.asc()) + elif order == ResultOrder.DURATION_DESC: + query = query.order_by( + (models.TrackCache.moving_time + models.TrackCache.stopped_time).desc() + ) + elif order == ResultOrder.DURATION_ASC: + query = query.order_by( + (models.TrackCache.moving_time + models.TrackCache.stopped_time).asc() + ) + return query + + @view_config( route_name="browse", renderer="fietsboek:templates/browse.jinja2", request_method="GET" ) @@ -357,7 +402,11 @@ def browse(request: Request) -> Response: # Build our query query = select(track).join(models.TrackCache, isouter=True) query = filters.compile(query, track, models.TrackCache) - query = query.order_by(track.date_raw.desc()) + + order = ResultOrder.DATE_DESC + if request.params.get("sort"): + order = _get_enum(ResultOrder, request.params.get("sort")) + query = apply_order(query, track, order) tracks = request.dbsession.execute(query).scalars() tracks = (TrackWithMetadata(track, request.data_manager) for track in tracks) -- cgit v1.2.3