Skip to content

Commit 825c21f

Browse files
authored
Merge pull request #166 from metabrainz/LB-1731
LB-1731: Create daily jams for users who don't have enough listens
2 parents c8b0e5f + 25acd75 commit 825c21f

File tree

4 files changed

+61
-8
lines changed

4 files changed

+61
-8
lines changed

tests/test_filters.py

Lines changed: 14 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -2,12 +2,22 @@
22

33
import requests_mock
44

5+
from troi.patch import Patch
56
from troi import Artist, ArtistCredit, Recording, Playlist
67
import troi.filters
78
from troi.musicbrainz.recording import RecordingListElement
89
from troi.listenbrainz.feedback import ListensFeedbackLookup
910

1011

12+
class DummyPatch:
13+
14+
def __init__(self):
15+
self.local_storage = {}
16+
17+
def set_patch_object(self, obj):
18+
pass
19+
20+
1121
class TestArtistCreditFilterElement(unittest.TestCase):
1222

1323
def test_artist_credit_filter_include(self):
@@ -56,6 +66,7 @@ def test_artist_credit_limiter_higher_ranked(self):
5666
Recording(mbid='73a9d0db-0ec7-490e-9a85-0525a5ccef8e', artist_credit=ArtistCredit(artist_credit_id=197), ranking=.1)
5767
]
5868

69+
5970
e = troi.filters.ArtistCreditLimiterElement(1)
6071
flist = e.read([rlist])
6172
assert len(flist) == 2
@@ -235,14 +246,17 @@ def test_never_listened_element(self):
235246
Recording(mbid='c88b3490-35a0-460d-b3bc-bc50c8855d00'),
236247
Recording(mbid='139654ae-2c02-4e0f-aee0-c47da6e59ff1', listenbrainz={"latest_listened_at": 345345345})
237248
]
249+
p = DummyPatch()
238250
e = troi.filters.NeverListenedFilterElement()
251+
e.set_patch_object(p)
239252
flist = e.read([rlist])
240253
assert len(flist) == 2
241254

242255
assert flist[0].mbid == '8756f690-18ca-488d-a456-680fdaf234bd'
243256
assert flist[1].mbid == '139654ae-2c02-4e0f-aee0-c47da6e59ff1'
244257

245258
e = troi.filters.NeverListenedFilterElement(remove_unlistened=False)
259+
e.set_patch_object(p)
246260
flist = e.read([rlist])
247261
assert len(flist) == 2
248262

troi/filters.py

Lines changed: 19 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -343,9 +343,12 @@ class LatestListenedAtFilterElement(troi.Element):
343343
:param min_number_of_days: The number of tracks that must have passed for a track to be kept.
344344
'''
345345

346-
def __init__(self, min_number_of_days=14):
346+
MIN_ITEMS_REQUIRED = 5
347+
348+
def __init__(self, min_number_of_days=14, keep_unlistened_if_empty=False):
347349
troi.Element.__init__(self)
348350
self.min_number_of_days = min_number_of_days
351+
self.keep_unlistened_if_empty = keep_unlistened_if_empty
349352

350353
@staticmethod
351354
def inputs():
@@ -369,6 +372,12 @@ def read(self, inputs):
369372
else:
370373
results.append(r)
371374

375+
if len(results) < self.MIN_ITEMS_REQUIRED and self.keep_unlistened_if_empty:
376+
self.local_storage["latest_listened_was_empty"] = True
377+
return recordings
378+
379+
self.local_storage["latest_listened_was_empty"] = False
380+
372381
return results
373382

374383

@@ -377,14 +386,17 @@ class NeverListenedFilterElement(troi.Element):
377386
Remove/keep only recordings if they have not/been listened to.
378387
'''
379388

380-
def __init__(self, remove_unlistened=True):
389+
MIN_ITEMS_REQUIRED = 5
390+
391+
def __init__(self, remove_unlistened=True, keep_unlistened_if_empty=False):
381392
'''
382393
Filter the recordings according to latest_listened_at field in the lb metadata.
383394
If that field is None, treat it as if the user hasn't listened to this track
384395
and remove it, or keep it, based on the remove_unlistened parameter.
385396
'''
386397
troi.Element.__init__(self)
387398
self.remove_unlistened = remove_unlistened
399+
self.keep_unlistened_if_empty = keep_unlistened_if_empty
388400

389401
@staticmethod
390402
def inputs():
@@ -411,8 +423,12 @@ def read(self, inputs):
411423
continue
412424
else:
413425
results.append(r)
414-
415426

427+
if len(results) < self.MIN_ITEMS_REQUIRED and self.keep_unlistened_if_empty:
428+
self.local_storage["never_listened_was_empty"] = True
429+
return recordings
430+
431+
self.local_storage["never_listened_was_empty"] = False
416432
return results
417433

418434

troi/patches/periodic_jams.py

Lines changed: 25 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -108,25 +108,32 @@ def create(self, inputs):
108108

109109
if jam_type in ("daily-jams", "weekly-jams"):
110110
# Remove tracks that have not been listened to before.
111-
never_listened = troi.filters.NeverListenedFilterElement()
111+
never_listened = troi.filters.NeverListenedFilterElement(keep_unlistened_if_empty=True)
112112
never_listened.set_sources(recent_listens_lookup)
113113
if jam_type == "daily-jams":
114114
jam_name = "Daily Jams"
115115
else:
116116
jam_name = "Weekly Jams"
117117
jam_date = "week of " + jam_date
118+
self.local_storage["jam_name"] = jam_name
119+
120+
latest_filter = troi.filters.LatestListenedAtFilterElement(DAYS_OF_RECENT_LISTENS_TO_EXCLUDE,
121+
keep_unlistened_if_empty=True)
122+
latest_filter.set_sources(never_listened)
118123
elif jam_type == "weekly-exploration":
119124
# Remove tracks that have been listened to before.
120125
never_listened = troi.filters.NeverListenedFilterElement(remove_unlistened=False)
121126
never_listened.set_sources(recent_listens_lookup)
122127
jam_name = "Weekly Exploration"
123128
jam_date = "week of " + jam_date
129+
self.local_storage["jam_name"] = jam_name
130+
131+
latest_filter = troi.filters.LatestListenedAtFilterElement(DAYS_OF_RECENT_LISTENS_TO_EXCLUDE)
132+
latest_filter.set_sources(never_listened)
133+
124134
else:
125135
raise RuntimeError("someone goofed up!")
126136

127-
latest_filter = troi.filters.LatestListenedAtFilterElement(DAYS_OF_RECENT_LISTENS_TO_EXCLUDE)
128-
latest_filter.set_sources(never_listened)
129-
130137
feedback_lookup = troi.listenbrainz.feedback.ListensFeedbackLookup(user_name, auth_token=inputs.get("token"))
131138
feedback_lookup.set_sources(latest_filter)
132139

@@ -137,7 +144,7 @@ def create(self, inputs):
137144
hate_filter.set_sources(recs_lookup)
138145

139146
pl_maker = PlaylistMakerElement(name="%s for %s, %s" % (jam_name, user_name, jam_date),
140-
desc="%s playlist!" % jam_name,
147+
desc=None,
141148
patch_slug=jam_type,
142149
max_num_recordings=50,
143150
max_artist_occurrence=2,
@@ -147,3 +154,16 @@ def create(self, inputs):
147154
pl_maker.set_sources(hate_filter)
148155

149156
return pl_maker
157+
158+
def post_process(self):
159+
"""
160+
Take the information saved in local_storage and create proper playlist names and descriptions.
161+
"""
162+
163+
jam_name = self.local_storage["jam_name"]
164+
if self.local_storage["latest_listened_was_empty"]:
165+
desc = f"This {jam_name} playlist includes tracks you've recently heard, as we don't have enough listens to create a better playlist."
166+
else:
167+
desc = f"We made this {jam_name} playlist from your recommended tracks to create a comfortable playlist of music you've not listened to recently."
168+
169+
self.local_storage["_playlist_desc"] = desc

troi/playlist.py

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -228,6 +228,9 @@ def print(self):
228228
continue
229229
self.print_recording.print(recording)
230230

231+
if playlist.description:
232+
logger.info("description: '%s'" % playlist.description)
233+
231234
def save(self, track_count=None, file_obj=None):
232235
"""Save each playlist to disk, giving each playlist a unique name if none was provided.
233236

0 commit comments

Comments
 (0)