refactor(discover): both affinities weigh a watch by one rule
The YouTube channel affinity and the Plex one each had the recency decay, the undated-watch rule and the scale to the strongest; app.draw now holds them. Tests hold that another user's history leans neither draw, the rail's blind slot, and that a show leans on show recommendations.
This commit is contained in:
@@ -10,7 +10,7 @@ history the order is exactly the old one. DB lane.
|
||||
import hashlib
|
||||
from datetime import datetime, timedelta, timezone
|
||||
|
||||
from sqlalchemy import text
|
||||
from sqlalchemy import select, text
|
||||
|
||||
from app import draw
|
||||
from app.catalog.models import Channel, Subscription, Video
|
||||
@@ -267,3 +267,69 @@ def test_the_discover_rail_leans_the_same_way(db, make_user):
|
||||
]
|
||||
assert leads.count("comedy_new") > 0
|
||||
assert leads.count("horror_new") + leads.count("tipped_new") > 3 * leads.count("comedy_new")
|
||||
|
||||
|
||||
# ── whose history: only the user's own ───────────────────────────────────────────────────────────
|
||||
|
||||
|
||||
def test_another_users_youtube_history_does_not_lean_my_draw(db, make_user):
|
||||
watcher = _seed_yt(db, make_user, watched_in_fav=12)
|
||||
me = make_user()
|
||||
db.add_all(
|
||||
[
|
||||
Subscription(user_id=me.id, channel_id="UCfav"),
|
||||
Subscription(user_id=me.id, channel_id="UCother"),
|
||||
]
|
||||
)
|
||||
db.commit()
|
||||
assert queries._channel_affinity(db, watcher)
|
||||
assert queries._channel_affinity(db, me) == {}
|
||||
|
||||
|
||||
def test_another_users_plex_history_does_not_lean_my_draw(db, make_user):
|
||||
_seed_plex(db, make_user, history=True)
|
||||
me = make_user()
|
||||
assert not affinity.for_user(db, me.id)
|
||||
keys = ["horror_new", "comedy_new", "tipped_new"] + [f"horror_seen{i}" for i in range(3)]
|
||||
for s in range(12):
|
||||
got = [c["id"] for c in queries.plex_surprise(db, me.id, "any", s, 6, lang="en")]
|
||||
assert got == _md5_order(keys, s)
|
||||
|
||||
|
||||
# ── the rail: its blind slot, and a show's own recommendations ───────────────────────────────────
|
||||
|
||||
|
||||
def test_the_rails_third_slot_is_the_blind_draw(db, make_user, monkeypatch):
|
||||
# The rail draws with random(), so the blend is watched rather than its odds: every leaning draw
|
||||
# goes through it, with the blind order beside the weighted one and the third slot blind.
|
||||
user, *_ = _seed_plex(db, make_user, history=True)
|
||||
calls = []
|
||||
real = draw.blend
|
||||
|
||||
def spy(weighted, blind, offset, n, key):
|
||||
calls.append((offset, n, sorted(map(key, weighted)), sorted(map(key, blind))))
|
||||
return real(weighted, blind, offset, n, key=key)
|
||||
|
||||
monkeypatch.setattr(draw, "blend", spy)
|
||||
got = plex_discover.surprise(db, user, "movie", "en", 3, [], [], LibraryFilters())["items"]
|
||||
assert calls and all(offset == 1 and n == 3 for offset, n, *_ in calls)
|
||||
pool = sorted(["horror_new", "comedy_new", "tipped_new"])
|
||||
assert calls[0][2] == pool and calls[0][3] == pool
|
||||
assert len(got) == 3
|
||||
|
||||
|
||||
def test_a_show_leans_on_the_shows_recommended_not_the_films(db, make_user):
|
||||
lib = PlexLibrary(plex_key="80", title="Shows", kind="show", enabled=True)
|
||||
db.add(lib)
|
||||
db.flush()
|
||||
tipped = PlexShow(rating_key="tv1", library_id=lib.id, title="t", guid="tmdb:1", genres=[])
|
||||
db.add(tipped)
|
||||
db.commit()
|
||||
shows = affinity.Affinity(show_recs={"tmdb:1": 1.0})
|
||||
films = affinity.Affinity(movie_recs={"tmdb:1": 1.0})
|
||||
|
||||
def weight(aff):
|
||||
return db.execute(select(aff.weight(PlexShow)).where(PlexShow.id == tipped.id)).scalar_one()
|
||||
|
||||
assert weight(shows) == 1 + affinity.REC_WEIGHT
|
||||
assert weight(films) == 1
|
||||
|
||||
Reference in New Issue
Block a user