diff options
| author | Daniel Schadt <kingdread@gmx.de> | 2023-05-09 19:54:21 +0200 | 
|---|---|---|
| committer | Daniel Schadt <kingdread@gmx.de> | 2023-05-09 19:54:21 +0200 | 
| commit | 6bb8904f65260d69b0382b6dea55ec52a16ba704 (patch) | |
| tree | e1db8f84195b8847a334ca33f61b860c76118498 | |
| parent | 4ad36ba65e33d32bd2ca1bae0cbc299b244369d8 (diff) | |
| download | fietsboek-6bb8904f65260d69b0382b6dea55ec52a16ba704.tar.gz fietsboek-6bb8904f65260d69b0382b6dea55ec52a16ba704.tar.bz2 fietsboek-6bb8904f65260d69b0382b6dea55ec52a16ba704.zip  | |
implement sorting for the browse page
| -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)  | 
