songs2slides

A tool that automatically finds song lyrics and creates lyric slideshows
git clone https://git.ashermorgan.net/songs2slides/
Log | Files | Refs | README

commit 2b6f2c24ce026d93da8a4bc3240943c214f1990d
parent ef8792327da870747c9cb569543a18d8e47b0fdc
Author: ashermorgan <59518073+ashermorgan@users.noreply.github.com>
Date:   Fri, 23 Feb 2024 09:04:13 -0800

Implement filter_lyrics function

Diffstat:
Msongs2slides/core.py | 33++++++++++++++++++++++++++++++++-
Mtests/test_core.py | 79+++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++------
2 files changed, 105 insertions(+), 7 deletions(-)

diff --git a/songs2slides/core.py b/songs2slides/core.py @@ -5,6 +5,7 @@ from pptx.dml.color import RGBColor from pptx.enum.text import MSO_ANCHOR, PP_ALIGN from pptx.util import Inches, Pt import os +import re import requests @dataclass @@ -26,6 +27,35 @@ class SongData: artist: str lyrics: str +def filter_lyrics(lyrics: str): + """ + Filter raw lyrics to remove text enclosed in brackets or parenthesis + + Used by get_song_data + + Parameters + ---------- + lyrics : str + The raw lyrics + + Returns + ------- + str + The filtered lyrics + """ + + filtered = '\n' + lyrics + '\n' + + # Remove enclosed text that takes up whole numbers of lines + filtered = re.sub(r'\n\[[^\]]*\]\n', '\n', filtered) + filtered = re.sub(r'\n\([^\)]*\)\n', '\n', filtered) + + # Remove enclosed text that takes up partial lines + filtered = re.sub(r'\[[^\]]*\]', '', filtered) + filtered = re.sub(r'\([^\)]*\)', '', filtered) + + return filtered.strip() + def get_song_data(title: str, artist:str): """ Get song data from an external API @@ -51,7 +81,8 @@ def get_song_data(title: str, artist:str): data = requests.get(url).json() if 'lyrics' in data.keys(): - return SongData(data['title'], data['artist'], data['lyrics']) + return SongData(data['title'], data['artist'], + filter_lyrics(data['lyrics'])) else: raise Exception() diff --git a/tests/test_core.py b/tests/test_core.py @@ -4,28 +4,95 @@ from unittest.mock import patch, call from songs2slides import core class TestCore(unittest.TestCase): + def test_filter_lyrics_inline(self): + # Declare raw lyrics and expected cleaned lyrics + lyrics = 'A[remove]B\nC(remove)D' + expected = 'AB\nCD' + + # Clean lyrics + result = core.filter_lyrics(lyrics) + + # Assert slides are correct + self.assertEqual(result, expected) + + def test_filter_lyrics_whole_lines(self): + # Declare raw lyrics and expected cleaned lyrics + lyrics = 'A\n[remove]\nB\n(remove)\nC' + expected = 'A\nB\nC' + + # Clean lyrics + result = core.filter_lyrics(lyrics) + + # Assert slides are correct + self.assertEqual(result, expected) + + def test_filter_lyrics_multiple_lines(self): + # Declare raw lyrics and expected cleaned lyrics + lyrics = 'A\n[re\nmove]\nB\n(re\nmove)\nC' + expected = 'A\nB\nC' + + # Clean lyrics + result = core.filter_lyrics(lyrics) + + # Assert slides are correct + self.assertEqual(result, expected) + + def test_filter_lyrics_blank_lines(self): + # Declare raw lyrics and expected cleaned lyrics + lyrics = 'A\n[remove]\n\n(remove)\nB' + expected = 'A\n\nB' + + # Clean lyrics + result = core.filter_lyrics(lyrics) + + # Assert slides are correct + self.assertEqual(result, expected) + + def test_filter_lyrics_all(self): + # Declare raw lyrics and expected cleaned lyrics + lyrics = 'A[remove]B\n[remove]\n\nC(remove)D\n(re\nmove)' + expected = 'AB\n\nCD' + + # Clean lyrics + result = core.filter_lyrics(lyrics) + + # Assert slides are correct + self.assertEqual(result, expected) + + def test_filter_lyrics_empty_string(self): + # Clean lyrics + result = core.filter_lyrics('') + + # Assert slides are correct + self.assertEqual(result, '') + def test_get_song_data_success(self): with patch('songs2slides.core.os.getenv') as mocked_env, \ - patch('songs2slides.core.requests.get') as mocked_get: + patch('songs2slides.core.requests.get') as mocked_get, \ + patch('songs2slides.core.filter_lyrics') as mocked_clean: - # Mock os.getenv and requests.get + # Mock os.getenv, requests.get, and core.filter_lyrics mocked_env.return_value = 'api://lyrics/{artist}/{title}' - mocked_get.return_value.text = b'{"lyrics":"A\nB\nC\nD","title":"Foo","artist":"Bar","image":null}' mocked_get.return_value.json.return_value = { - 'lyrics': 'A\nB\nC\nD', + 'lyrics': 'raw', 'title': 'Foo', 'artist': 'Bar', } mocked_get.return_value.status_code = 200 + mocked_clean.return_value = 'clean' # Get song data song_data = core.get_song_data('foo', 'bar') - # Assert song data is correct + # Assert mocked methods were used correctly + mocked_env.assert_called_with('API_URL') mocked_get.assert_called_with('api://lyrics/bar/foo') + mocked_clean.assert_called_with('raw') + + # Assert song data is correct self.assertEqual(song_data.title, 'Foo') self.assertEqual(song_data.artist, 'Bar') - self.assertEqual(song_data.lyrics, 'A\nB\nC\nD') + self.assertEqual(song_data.lyrics, 'clean') def test_get_song_data_no_url(self): with patch('songs2slides.core.os.getenv') as mocked_env, \