149 lines
9 KiB
Python
149 lines
9 KiB
Python
"""Stöbern (podcasts/discover.py): the full-text index, similar episodes, people, an episode
|
|
from outside onto the Merkliste."""
|
|
import json
|
|
from datetime import datetime, timedelta, timezone
|
|
from unittest.mock import patch
|
|
|
|
from django.contrib.auth.models import User
|
|
from django.core.cache import cache
|
|
from django.test import TestCase
|
|
|
|
from . import discover, people
|
|
from .models import EpisodeProgress, PodcastEpisode, PodcastFeed, PodcastLater
|
|
|
|
|
|
class PeopleTests(TestCase):
|
|
def test_names_from_titles(self):
|
|
eps = [
|
|
(1, 1, 'Episode 651: Die Brandmauer mit Peter Decker'),
|
|
(2, 1, 'Episode 568: Trumps Zollkrieg mit Peter Decker'),
|
|
(3, 2, 'Imperialismus heute - Peter Decker - 2006'),
|
|
(4, 3, 'Gespräch mit Peter Schadt über Arbeit'),
|
|
(5, 3, 'Ulrike Eifler im Gespräch mit Peter Thiel'),
|
|
(6, 4, 'Was tun? Mit Ulrike Herrmann'),
|
|
(9, 4, 'Streik! Diskussion mit Ulrike Eifler'),
|
|
(7, 5, 'Gespräch mit Ulrike Herrmann über das Ende des Kapitalismus'),
|
|
(8, 6, 'Live aus Berlin: Die Linke und Friedrich Merz'),
|
|
]
|
|
found = {p['name']: p for p in people.people(eps)}
|
|
self.assertEqual(found['Peter Decker']['episodes'], 3)
|
|
self.assertEqual(found['Peter Decker']['feeds'], 2)
|
|
self.assertIn('Ulrike Herrmann', found)
|
|
self.assertNotIn('Die Brandmauer', found)
|
|
self.assertNotIn('Die Linke', found)
|
|
|
|
|
|
class IndexTests(TestCase):
|
|
def setUp(self):
|
|
cache.clear()
|
|
self.user = User.objects.create_user(username='finder', password='pw12345678')
|
|
self.client.force_login(self.user)
|
|
self.other = User.objects.create_user(username='other', password='pw12345678')
|
|
self.f1 = PodcastFeed.objects.create(user=self.user, rss_url='https://example.org/1', title='Eins')
|
|
self.f2 = PodcastFeed.objects.create(user=self.user, rss_url='https://example.org/2', title='Zwei')
|
|
self.f3 = PodcastFeed.objects.create(user=self.user, rss_url='https://example.org/3', title='Drei',
|
|
new_episodes=PodcastFeed.NEW_IGNORE)
|
|
theirs = PodcastFeed.objects.create(user=self.other, rss_url='https://example.org/1', title='Eins')
|
|
|
|
def ep(feed, guid, title, desc=''):
|
|
return PodcastEpisode.objects.create(feed=feed, guid=guid, title=title, description=desc,
|
|
audio_url=f'https://example.org/{guid}.mp3',
|
|
pub_date=datetime(2026, 1, 1, tzinfo=timezone.utc))
|
|
self.a = ep(self.f1, 'a', 'Über die Ökonomie der Plattformen', '<p>Mit Gästen über <b>Plattformkapitalismus</b> und Arbeit.</p>')
|
|
self.b = ep(self.f2, 'b', 'Plattformkapitalismus erklärt', 'Wie Plattformen Arbeit organisieren.')
|
|
self.c = ep(self.f2, 'c', 'Gartenarbeit im Herbst', 'Tomaten, Kürbisse und Laub.')
|
|
self.d = ep(self.f3, 'd', 'Noch mehr Plattformkapitalismus', 'Auf Abruf.')
|
|
for i in range(30): # words everywhere say nothing
|
|
ep(self.f1, f'x{i}', f'Folge {i}', 'Arbeit und Gesellschaft')
|
|
ep(theirs, 't', 'Plattformkapitalismus bei jemand anderem', '')
|
|
self.assertGreaterEqual(discover.index_new(), 35)
|
|
self.assertEqual(discover.index_new(), 0, 'nothing twice')
|
|
|
|
def test_search_titles_shownotes_prefix_folding(self):
|
|
r = self.client.get('/podcasts/discover/search/?q=plattformkap').json()
|
|
self.assertEqual({e['id'] for e in r['episodes']}, {self.a.id, self.b.id, self.d.id})
|
|
self.assertEqual(r['episodes'][0]['id'] in (self.b.id, self.d.id), True, 'a title match first')
|
|
r = self.client.get('/podcasts/discover/search/?q=okonomie').json()
|
|
self.assertEqual([e['id'] for e in r['episodes']], [self.a.id])
|
|
r = self.client.get('/podcasts/discover/search/?q=plattformkapitalismus&stream=1').json()
|
|
self.assertNotIn(self.d.id, [e['id'] for e in r['episodes']], 'Auf Abruf is not in the Strom')
|
|
|
|
def test_similar_from_other_podcasts(self):
|
|
r = self.client.get(f'/podcasts/discover/similar/{self.b.id}/').json()
|
|
ids = [e['id'] for e in r['episodes']]
|
|
self.assertIn(self.a.id, ids)
|
|
self.assertNotIn(self.c.id, ids, 'same podcast, and about gardens')
|
|
self.assertEqual(self.client.get('/podcasts/discover/similar/999999/').status_code, 404)
|
|
|
|
def test_person_episodes(self):
|
|
PodcastEpisode.objects.create(feed=self.f1, guid='p', title='Arbeit mit Peter Decker', audio_url='https://example.org/p.mp3')
|
|
r = self.client.get('/podcasts/discover/person/?name=Peter Decker').json()
|
|
self.assertEqual([e['title'] for e in r['episodes']], ['Arbeit mit Peter Decker'])
|
|
|
|
def test_forgotten(self):
|
|
old = PodcastEpisode.objects.create(feed=self.f2, guid='old', title='Alt', audio_url='https://example.org/old.mp3',
|
|
pub_date=datetime.now(timezone.utc) - timedelta(days=200))
|
|
for e in (self.b, self.c, self.a):
|
|
EpisodeProgress.objects.create(user=self.user, episode=e, played=True)
|
|
PodcastEpisode.objects.create(feed=self.f2, guid='h1', title='H1', audio_url='https://example.org/h1.mp3')
|
|
heard = PodcastEpisode.objects.create(feed=self.f2, guid='h2', title='H2', audio_url='https://example.org/h2.mp3')
|
|
EpisodeProgress.objects.create(user=self.user, episode=heard, played=True)
|
|
r = self.client.get('/podcasts/discover/forgotten/').json()
|
|
self.assertEqual([e['id'] for e in r['loved']], [old.id])
|
|
|
|
|
|
class OutsideTests(TestCase):
|
|
def setUp(self):
|
|
cache.clear()
|
|
self.user = User.objects.create_user(username='out', password='pw12345678')
|
|
self.client.force_login(self.user)
|
|
PodcastFeed.objects.create(user=self.user, rss_url='https://mine.example/feed', title='Mein Podcast')
|
|
|
|
def test_elsewhere_keeps_only_the_person_and_unknown_podcasts(self):
|
|
apple = {'results': [
|
|
{'collectionName': 'Fremd', 'feedUrl': 'https://other.example/feed', 'trackName': 'Gespräch mit Peter Decker',
|
|
'episodeUrl': 'https://other.example/1.mp3', 'episodeGuid': 'g1', 'releaseDate': '2026-01-02T00:00:00Z',
|
|
'trackTimeMillis': 60000},
|
|
{'collectionName': 'Mein Podcast', 'feedUrl': 'https://mine.example/feed', 'trackName': 'Mit Peter Decker',
|
|
'episodeUrl': 'https://mine.example/1.mp3'},
|
|
{'collectionName': 'Fremd', 'feedUrl': 'https://other.example/feed', 'trackName': 'Peter Pan',
|
|
'episodeUrl': 'https://other.example/2.mp3'},
|
|
]}
|
|
|
|
def fake(url, **params):
|
|
return apple if 'itunes' in url else {'data': []}
|
|
with patch.object(discover, '_get_json', fake):
|
|
r = self.client.get('/podcasts/discover/elsewhere/?name=Peter Decker').json()
|
|
self.assertEqual([e['title'] for e in r['episodes']], ['Gespräch mit Peter Decker'])
|
|
self.assertEqual(r['episodes'][0]['duration_seconds'], 60)
|
|
|
|
def test_take_subscribes_auf_abruf_and_merklists(self):
|
|
item = {'podcast': 'Fremd', 'feed_url': 'https://other.example/feed', 'title': 'Gespräch mit Peter Decker',
|
|
'audio_url': 'https://other.example/1.mp3', 'guid': 'g1', 'pub_date': '2026-01-02T00:00:00Z',
|
|
'duration_seconds': 60}
|
|
with patch('podcasts.views._refresh_feed', side_effect=OSError('offline')):
|
|
r = self.client.post('/podcasts/discover/take/', json.dumps(item), content_type='application/json').json()
|
|
self.assertTrue(r['subscribed'])
|
|
feed = PodcastFeed.objects.get(pk=r['feed_id'])
|
|
self.assertEqual(feed.new_episodes, PodcastFeed.NEW_IGNORE)
|
|
self.assertTrue(PodcastLater.objects.filter(user=self.user, episode_id=r['episode_id']).exists())
|
|
with patch('podcasts.views._refresh_feed', side_effect=OSError('offline')):
|
|
r2 = self.client.post('/podcasts/discover/take/', json.dumps(item), content_type='application/json').json()
|
|
self.assertEqual((r2['episode_id'], r2['subscribed']), (r['episode_id'], False))
|
|
|
|
def test_recommend_counts_and_leaves_out_mine(self):
|
|
EpisodeProgress.objects.create(
|
|
user=self.user, played=True,
|
|
episode=PodcastEpisode.objects.create(feed=PodcastFeed.objects.get(title='Mein Podcast'), guid='m',
|
|
title='M', audio_url='https://mine.example/m.mp3'))
|
|
|
|
def fake(url, **params):
|
|
if url.endswith('search/podcast'):
|
|
return {'data': [{'id': 7, 'title': 'Mein Podcast', 'xmlURL': 'https://mine.example/feed'}]}
|
|
if url.endswith('recommend'):
|
|
return {'data': [{'id': 8, 'title': 'Neu', 'xmlURL': 'https://new.example/feed'},
|
|
{'id': 7, 'title': 'Mein Podcast', 'xmlURL': 'https://mine.example/feed'}]}
|
|
return None
|
|
with patch.object(discover, '_get_json', fake):
|
|
r = self.client.get('/podcasts/discover/recommend/').json()
|
|
self.assertEqual([(p['title'], p['because']) for p in r['podcasts']], [('Neu', ['Mein Podcast'])])
|