From 4e75017df690bec205be5b9b192106bfd9aa9c3e Mon Sep 17 00:00:00 2001
From: xenofem <xenofem@xeno.science>
Date: Tue, 19 Mar 2024 14:28:29 -0400
Subject: [PATCH 1/2] add page counts to work info tables

---
 dlibrary/static/dlibrary.css | 12 ++++++++----
 dlibrary/templates/work.html | 20 ++++++++++++--------
 2 files changed, 20 insertions(+), 12 deletions(-)

diff --git a/dlibrary/static/dlibrary.css b/dlibrary/static/dlibrary.css
index 00c1846..52f3696 100644
--- a/dlibrary/static/dlibrary.css
+++ b/dlibrary/static/dlibrary.css
@@ -139,19 +139,19 @@ body {
 
 .work-info td, .work-info th {
     vertical-align: top;
+    padding-top: 5px;
 }
 
-.work-info td {
-    padding-top: 5px;
+.work-info tr.bubbles th {
+    padding-top: 14px;
 }
 
 .work-info th {
     text-align: right;
     padding-right: 10px;
-    padding-top: 10px;
 }
 
-.work-info-link {
+.bubbles a {
     background: #333;
     padding: 5px;
     border-radius: 5px;
@@ -159,6 +159,10 @@ body {
     margin-bottom: 5px;
 }
 
+.work-info table {
+    margin-bottom: 15px;
+}
+
 #suggested-subheader {
     text-align: center;
 }
diff --git a/dlibrary/templates/work.html b/dlibrary/templates/work.html
index e622c0b..e3aae67 100644
--- a/dlibrary/templates/work.html
+++ b/dlibrary/templates/work.html
@@ -12,29 +12,33 @@
   <div class="work-info">
     <table>
       {% if work['circle'] %}
-      <tr>
+      <tr class="bubbles">
         <th>Circle</th>
-        <td><a class="work-info-link" href="{{ root() }}/circles/{{ urlcat(work['circle']) }}/{{ index() }}">{{ work['circle'] }}</a></td>
+        <td><a href="{{ root() }}/circles/{{ urlcat(work['circle']) }}/{{ index() }}">{{ work['circle'] }}</a></td>
       </tr>
       {% endif %}
       {% if work['authors'] %}
-      <tr>
+      <tr class="bubbles">
         <th>Authors</th>
-        <td>{% for author in work['authors'] %}<a class="work-info-link" href="{{ root() }}/authors/{{ urlcat(author) }}/{{ index() }}">{{ author }}</a> {% endfor %}</td>
+        <td>{% for author in work['authors'] %}<a href="{{ root() }}/authors/{{ urlcat(author) }}/{{ index() }}">{{ author }}</a> {% endfor %}</td>
       </tr>
       {% endif %}
       {% if work['tags'] %}
-      <tr>
+      <tr class="bubbles">
         <th>Tags</th>
-        <td>{% for tag in work['tags'] %}<a class="work-info-link" href="{{ root() }}/tags/{{ urlcat(tag) }}/{{ index() }}">{{ tag }}</a> {% endfor %}</td>
+        <td>{% for tag in work['tags'] %}<a href="{{ root() }}/tags/{{ urlcat(tag) }}/{{ index() }}">{{ tag }}</a> {% endfor %}</td>
       </tr>
       {% endif %}
       {% if work['series'] %}
-      <tr>
+      <tr class="bubbles">
         <th>Series</th>
-        <td><a class="work-info-link" href="{{ root() }}/series/{{ urlcat(work['series']) }}/{{ index() }}">{{ work['series'] }}</a></td>
+        <td><a href="{{ root() }}/series/{{ urlcat(work['series']) }}/{{ index() }}">{{ work['series'] }}</a></td>
       </tr>
       {% endif %}
+      <tr>
+        <th>Pages</th>
+        <td>{{ work['images'] | length }}</td>
+      </tr>
     </table>
     {% if work['description'] %}
     {{ work['description'] }}

From f66fa8138d543cc3621707ddd4e1d2fac76ce8cd Mon Sep 17 00:00:00 2001
From: xenofem <xenofem@xeno.science>
Date: Tue, 19 Mar 2024 15:12:49 -0400
Subject: [PATCH 2/2] show more suggested works, and break ties randomly
 instead of first-come-first-served

---
 dlibrary/dlibrary.py | 20 ++++++++++++++------
 1 file changed, 14 insertions(+), 6 deletions(-)

diff --git a/dlibrary/dlibrary.py b/dlibrary/dlibrary.py
index 096a623..88823e7 100755
--- a/dlibrary/dlibrary.py
+++ b/dlibrary/dlibrary.py
@@ -7,6 +7,7 @@ from io import BytesIO
 from pathlib import Path
 import os
 from os.path import relpath, splitext
+import random
 import re
 import readline
 import shutil
@@ -91,6 +92,8 @@ MULTIPART_RAR_TAIL_REGEX = re.compile(r'^(.+)\.part0*([^1]|[^0].+)\.rar$', re.I)
 PDF_REFERENCED_IMAGE_REGEX = re.compile(r'(^|(?<=\s))/(?P<ref_name>\S+)\s+Do($|(?=\s))')
 PDF_INLINE_IMAGE_REGEX = re.compile(r'(^|\s)(BI|ID|EI)($|\s)')
 
+SUGGESTED_WORKS_COUNT = 10
+
 debug_mode = False
 def debug(s):
     if debug_mode:
@@ -1012,18 +1015,23 @@ def similarity(a, b):
     memoized_similarities[(shorter, longer)] = result
     return result
 
-def top(items, n, key):
+def top(items, n, key, overflow=0):
     winners = []
     for item in items:
         score = key(item)
-        if len(winners) < n or score > winners[-1][1]:
+        if len(winners) < n or score >= winners[-1][1]:
             for i in range(len(winners) + 1):
-                if i == len(winners) or score > winners[i][1]:
+                if i == len(winners) or score >= winners[i][1]:
                     winners.insert(i, (item, score))
                     break
-            while len(winners) > n:
+            while len(winners) > n and winners[-1][1] < winners[n-1][1]:
                 winners.pop()
-    return [item for (item, score) in winners]
+
+    # shuffle followed by stable sort to randomly shuffle within each score tier
+    random.shuffle(winners)
+    winners.sort(key=lambda w: w[1], reverse=True)
+
+    return [item for (item, score) in winners[:n+overflow]]
 
 def generate(args):
     jenv = Environment(
@@ -1080,7 +1088,7 @@ def generate(args):
             if work['series'] and work['series'] == other_work['series']:
                 return -1
             return similarity(work['title'], other_work['title'])
-        suggested = top(works, 6, suggestion_priority)
+        suggested = top(works, SUGGESTED_WORKS_COUNT, suggestion_priority)
 
         work_dir = site_dir / 'works' / work['id']
         viewer_dir = work_dir / 'view'