Sortable triage, and say which sort is active
The list was ordered by max(interest, importance) and nothing on the page said so, which makes it impossible to tell whether an odd ranking is a bug or the intended order. Five orders, with the active one explained in a line beneath the control: best highest of the two scores (unchanged default) interest purely the taste profile, importance ignored entirely important purely the importance axis -- velocity, corroboration, prominence new most recently seen, regardless of score covered most independent newsrooms, syndicated copy collapsed "My interests" is the one that was actually missing. The two-axis design means a quiet story that is very much the reader's can sit below a big one that is not, and until now there was no way to see past that. An unknown sort falls back to the default rather than erroring.
This commit is contained in:
1 parent
90077aa3bd
commit
161c518aa7
3 files changed
+104
-7
No files matched your search
@@ -302,3 +302,55 @@ def test_triage_cards_expand_to_every_report(tmp_path, monkeypatch):
|
||||
assert f"Report number {n}" in html, f"report {n} missing from the card"
|
||||
assert "<details" in html and "4 more reports" in html
|
||||
assert "across 7 outlets" in html
|
||||
|
||||
|
||||
def _triage_db(tmp_path, monkeypatch):
|
||||
"""Three clusters that rank differently under each sort order."""
|
||||
import numpy as np
|
||||
from datetime import UTC, datetime, timedelta
|
||||
|
||||
monkeypatch.setenv("NEWS_DB", str(tmp_path / "s.db"))
|
||||
monkeypatch.setenv("NEWS_FEEDBACK_SECRET", SECRET)
|
||||
con = store.connect(tmp_path / "s.db")
|
||||
now = datetime.now(UTC)
|
||||
for n in range(3):
|
||||
store.upsert_source(con, id=f"s{n}", name=f"O{n}", url=f"http://s{n}", kind="rss",
|
||||
bucket=f"b{n}", weight=1.0, prominence=0, enabled=1,
|
||||
poll_interval_secs=900)
|
||||
# c_old is oldest, c_new is newest.
|
||||
for cid, age_h in (("c_old", 30), ("c_mid", 10), ("c_new", 1)):
|
||||
when = (now - timedelta(hours=age_h)).isoformat()
|
||||
con.execute("""INSERT INTO items (id,source_id,title,summary,url,canonical_url,
|
||||
published_at,first_seen_at,content_hash)
|
||||
VALUES (?,?,?,'',?,?,?,?,?)""",
|
||||
(cid, "s0", f"Story {cid}", f"http://x/{cid}", f"http://x/{cid}",
|
||||
when, when, cid))
|
||||
con.execute("""INSERT INTO clusters (id,centroid,size,first_seen_at,last_seen_at,
|
||||
lead_item_id) VALUES (?,?,1,?,?,?)""",
|
||||
(cid, store.to_blob(np.zeros(384, dtype="float32")), when, when, cid))
|
||||
con.execute("INSERT INTO cluster_members VALUES (?,?,?)", (cid, cid, when))
|
||||
from news import web
|
||||
web._VECTORS = None
|
||||
return TestClient(web.app)
|
||||
|
||||
|
||||
def test_newest_first_orders_by_recency(tmp_path, monkeypatch):
|
||||
html = _triage_db(tmp_path, monkeypatch).get("/?sort=new&show=all").text
|
||||
order = [t for t in ("c_new", "c_mid", "c_old") if f"Story {t}" in html]
|
||||
positions = [html.index(f"Story {t}") for t in order]
|
||||
assert positions == sorted(positions), "newest-first must be strictly by recency"
|
||||
assert html.index("Story c_new") < html.index("Story c_old")
|
||||
|
||||
|
||||
def test_sort_control_offers_every_order_and_marks_the_active_one(tmp_path, monkeypatch):
|
||||
c = _triage_db(tmp_path, monkeypatch)
|
||||
html = c.get("/?sort=interest").text
|
||||
for value in ("best", "interest", "important", "new", "covered"):
|
||||
assert f'value="{value}"' in html, f"sort option {value} missing"
|
||||
assert 'value="interest" selected' in html
|
||||
assert "Ignores importance entirely" in html, "the active sort must explain itself"
|
||||
|
||||
|
||||
def test_unknown_sort_falls_back_rather_than_erroring(tmp_path, monkeypatch):
|
||||
html = _triage_db(tmp_path, monkeypatch).get("/?sort=../../etc/passwd").text
|
||||
assert 'value="best" selected' in html
|
||||
Reference in new issue
Block a user