aboutsummaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--fietsboek/templates/browse.jinja210
-rw-r--r--fietsboek/views/browse.py53
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") }} &ShortDownArrow;</option>
+ <option value="DATE_ASC" {% if request.params.get("sort") == "DATE_ASC" %}selected{% endif %}>{{ _("page.browse.sort.date") }} &ShortUpArrow;</option>
+ <option value="LENGTH_DESC" {% if request.params.get("sort") == "LENGTH_DESC" %}selected{% endif %}>{{ _("page.browse.sort.length") }} &ShortDownArrow;</option>
+ <option value="LENGTH_ASC" {% if request.params.get("sort") == "LENGTH_ASC" %}selected{% endif %}>{{ _("page.browse.sort.length") }} &ShortUpArrow;</option>
+ <option value="DURATION_DESC" {% if request.params.get("sort") == "DURATION_DESC" %}selected{% endif %}>{{ _("page.browse.sort.duration") }} &ShortDownArrow;</option>
+ <option value="DURATION_ASC" {% if request.params.get("sort") == "DURATION_ASC" %}selected{% endif %}>{{ _("page.browse.sort.duration") }} &ShortUpArrow;</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)