diff options
-rw-r--r-- | fietsboek/templates/browse.jinja2 | 10 | ||||
-rw-r--r-- | 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") }} </button> </div> + <div class="col"> + <select class="form-select" name="sort"> + <option value="DATE_DESC" {% if request.params.get("sort") == "DATE_DESC" %}selected{% endif %}>{{ _("page.browse.sort.date") }} ↓</option> + <option value="DATE_ASC" {% if request.params.get("sort") == "DATE_ASC" %}selected{% endif %}>{{ _("page.browse.sort.date") }} ↑</option> + <option value="LENGTH_DESC" {% if request.params.get("sort") == "LENGTH_DESC" %}selected{% endif %}>{{ _("page.browse.sort.length") }} ↓</option> + <option value="LENGTH_ASC" {% if request.params.get("sort") == "LENGTH_ASC" %}selected{% endif %}>{{ _("page.browse.sort.length") }} ↑</option> + <option value="DURATION_DESC" {% if request.params.get("sort") == "DURATION_DESC" %}selected{% endif %}>{{ _("page.browse.sort.duration") }} ↓</option> + <option value="DURATION_ASC" {% if request.params.get("sort") == "DURATION_ASC" %}selected{% endif %}>{{ _("page.browse.sort.duration") }} ↑</option> + </select> + </div> </div> </form> </div> 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) |