Repository: internetarchive/openlibrary

Issue:
Tweak trending API: Sort by date
<!-- A clear and concise description of what the problem is. Ex. I'm always frustrated when [...] -->

The Trending carousel biases towards the same books, creating a re-enforcing algorithm. Use the date to help randomize results (i.e. sort most logged books by date instead of count)

### Describe the problem that you'd like solved
<!-- A clear and concise description of what you want to happen. -->


https://github.com/internetarchive/openlibrary/blob/fd990d519672ab74c3ba4a45916b504622d53d12/openlibrary/views/loanstats.py#L49-L65
and
https://github.com/internetarchive/openlibrary/blob/fd990d519672ab74c3ba4a45916b504622d53d12/openlibrary/core/bookshelves.py#L88-L107

Add a parameter to `most_logged_books` to sort most logged books secondarily by date (which sorts results by date rather than most logged). See: https://stackoverflow.com/questions/2051162/sql-multiple-column-ordering


### Proposal & Constraints
<!-- What is the proposed solution / implementation? Is there a precedent of this approach succeeding elsewhere? -->

<!-- Which suggestions or requirements should be considered for how feature needs to appear or be implemented? -->

### Additional context
<!-- Add any other context or screenshots about the feature request here. -->

### Stakeholders
<!-- @ tag stakeholders of this bug -->





Discussion:

Resolving patch:
diff --git a/openlibrary/core/bookshelves.py b/openlibrary/core/bookshelves.py
--- a/openlibrary/core/bookshelves.py
+++ b/openlibrary/core/bookshelves.py
@@ -94,6 +94,8 @@ def most_logged_books(
         since: date | None = None,
         page: int = 1,
         fetch: bool = False,
+        sort_by_count: bool = True,
+        minimum: int = 0,
     ) -> list:
         """Returns a ranked list of work OLIDs (in the form of an integer --
         i.e. OL123W would be 123) which have been most logged by
@@ -106,8 +108,15 @@ def most_logged_books(
         where = 'WHERE bookshelf_id' + ('=$shelf_id' if shelf_id else ' IS NOT NULL ')
         if since:
             where += ' AND created >= $since'
-        query = f'select work_id, count(*) as cnt from bookshelves_books {where}'
-        query += ' group by work_id order by cnt desc limit $limit offset $offset'
+        group_by = 'group by work_id'
+        if minimum:
+            group_by += f" HAVING COUNT(*) > {minimum}"
+        order_by = 'order by cnt desc' if sort_by_count else ''
+        query = f"""
+            select work_id, count(*) as cnt
+            from bookshelves_books
+            {where} {group_by} {order_by}
+            limit $limit offset $offset"""
         logger.info("Query: %s", query)
         data = {'shelf_id': shelf_id, 'limit': limit, 'offset': offset, 'since': since}
         logged_books = list(oldb.query(query, vars=data))
diff --git a/openlibrary/plugins/openlibrary/api.py b/openlibrary/plugins/openlibrary/api.py
--- a/openlibrary/plugins/openlibrary/api.py
+++ b/openlibrary/plugins/openlibrary/api.py
@@ -71,7 +71,9 @@ def GET(self, period="/daily"):
         from openlibrary.views.loanstats import SINCE_DAYS
 
         period = period[1:]  # remove slash
-        i = web.input(page=1, limit=100, days=0, hours=0)
+        i = web.input(
+            page=1, limit=100, days=0, hours=0, sort_by_count=False, minimum=0
+        )
         days = SINCE_DAYS.get(period, int(i.days))
         works = get_trending_books(
             since_days=days,
@@ -79,6 +81,8 @@ def GET(self, period="/daily"):
             limit=int(i.limit),
             page=int(i.page),
             books_only=True,
+            sort_by_count=not i.sort_by_count == 'false',
+            minimum=i.minimum,
         )
         result = {
             'query': f"/trending/{period}",
diff --git a/openlibrary/views/loanstats.py b/openlibrary/views/loanstats.py
--- a/openlibrary/views/loanstats.py
+++ b/openlibrary/views/loanstats.py
@@ -47,7 +47,15 @@ def reading_log_summary():
 
 
 @public
-def get_trending_books(since_days=1, since_hours=0, limit=18, page=1, books_only=False):
+def get_trending_books(
+    since_days=1,
+    since_hours=0,
+    limit=18,
+    page=1,
+    books_only=False,
+    sort_by_count=True,
+    minimum=None,
+):
     logged_books = (
         Bookshelves.fetch(get_activity_stream(limit=limit, page=page))  # i.e. "now"
         if (since_days == 0 and since_hours == 0)
@@ -56,6 +64,8 @@ def get_trending_books(since_days=1, since_hours=0, limit=18, page=1, books_only
             limit=limit,
             page=page,
             fetch=True,
+            sort_by_count=sort_by_count,
+            minimum=minimum,
         )
     )
     return (


Test patch: