todo.sr.ht: Ticket ordering v1 PROPOSED

This was lost somewhere along the way so this adds `order:id` and
`order:updated` ordering filters.

Reuses the negation logic to inverse sort direction, i.e. `!order:id`
and `!order:updated` to sort descending.

This could be made more general and integrated into core but I'm unsure
if we need anything more complicated like sorting by comment count or
similar which would require a more elaborate solution. So this is a
simple solution for todo only.

Ivan Habunek (1):
  Implement ticket ordering

 todosrht/blueprints/tracker.py |  3 +--
 todosrht/search.py             | 32 +++++++++++++++++++++++++++++---
 2 files changed, 30 insertions(+), 5 deletions(-)

Export patchset (mbox)
How do I use this?

Copy & paste the following snippet into your terminal to import this patchset into git:

curl -s https://lists.sr.ht/~sircmpwn/sr.ht-dev/patches/10691/mbox | git am -3
Learn more about email & git
View this thread in the archives

[PATCH todo.sr.ht 1/1] Implement ticket ordering Export this patch

 todosrht/blueprints/tracker.py |  3 +--
 todosrht/search.py             | 32 +++++++++++++++++++++++++++++---
 2 files changed, 30 insertions(+), 5 deletions(-)

diff --git a/todosrht/blueprints/tracker.py b/todosrht/blueprints/tracker.py
index 6ef75fc..34e9fc2 100644
--- a/todosrht/blueprints/tracker.py
+++ b/todosrht/blueprints/tracker.py
@@ -74,8 +74,7 @@ def return_tracker(tracker, access, **kwargs):
    tickets = (Ticket.query
        .filter(Ticket.tracker_id == tracker.id)

        terms = request.args.get("search")
diff --git a/todosrht/search.py b/todosrht/search.py
index 0cf673f..6d6de2d 100644
--- a/todosrht/search.py
+++ b/todosrht/search.py
@@ -62,14 +62,35 @@ def default_filter(value):

def apply_ordering(query, terms, column_map):
    for term in terms:
        if term.value not in column_map:
            valid = ", ".join(f"'{c}'" for c in column_map.keys())
            raise ValueError(
                f"Invalid {term.key} value: '{term.value}'. "
                f"Supported values are: {valid}."

        column = column_map[term.value]
        ordering = column.desc() if term.inverse else column.asc()
        query = query.order_by(ordering)

    return query

def apply_search(query, search_string, current_user):
    terms = list(search.parse_terms(search_string))
    order_terms = [t for t in terms if t.key == "order"]
    search_terms = [t for t in terms if t.key != "order"]

    # If search does not include a status filter, show open tickets
    if not any([term.key == "status" for term in terms]):
        terms.append(search.Term("status", "open", False))
    if not any([term.key == "status" for term in search_terms]):
        search_terms.append(search.Term("status", "open", False))

    return search.apply_terms(query, terms, default_filter, key_fns={
    # Set default ordering to 'updated desc' if not specified
    if not order_terms:
        order_terms = [search.Term("order", "updated", True)]

    query = search.apply_terms(query, search_terms, default_filter, key_fns={
        "status": status_filter,
        "submitter": lambda v: submitter_filter(v, current_user),
        "assigned": lambda v: asignee_filter(v, current_user),
@@ -77,6 +98,11 @@ def apply_search(query, search_string, current_user):
        "no": no_filter,

    return apply_ordering(query, order_terms, {
        "id": Ticket.scoped_id,
        "updated": Ticket.updated,

def find_usernames(query, limit=20):
    """Given a partial username string, returns matching usernames."""
    if not query or query == '~':