commit a581f843a445f279e44a2debb722665153e78838
parent 600f02e3c109462ae6655fe0344dcb0541c3f21a
Author: ashermorgan <59518073+ashermorgan@users.noreply.github.com>
Date: Mon, 19 Feb 2024 21:34:42 -0800
Implement get_slide_contents function
Diffstat:
2 files changed, 128 insertions(+), 11 deletions(-)
diff --git a/songs2slides/utils.py b/songs2slides/utils.py
@@ -1,14 +1,16 @@
from dotenv import load_dotenv
+from dataclasses import dataclass
import os
import requests
+@dataclass
class SongData:
"""
Represents data about a song
- Attributes:
- -----------
+ Attributes
+ ----------
title : str
The title of the song
artist : str
@@ -17,17 +19,16 @@ class SongData:
The song's lyrics, with double newlines separating stanzas
"""
- def __init__(self, title: str, artist: str, lyrics: str):
- self.title = title
- self.artist = artist
- self.lyrics = lyrics
+ title: str
+ artist: str
+ lyrics: str
def get_song_data(title: str, artist:str):
"""
Get song data from an external API
- Parameters:
- -----------
+ Parameters
+ ----------
title : str
The title of the song
artist : str
@@ -50,3 +51,49 @@ def get_song_data(title: str, artist:str):
return SongData(data['title'], data['artist'], data['lyrics'])
else:
raise Exception()
+
+def get_slide_contents(lyrics: str, lines_per_slide: int = 4):
+ """
+ Generate slide contents from song lyrics
+
+ Parameters
+ ----------
+ lyrics : str
+ The song lyrics
+ lines_per_slide : int
+ The maximum number of lines per slide (the default is 4)
+
+ Returns
+ -------
+ list of str
+ The list of slide contents
+ """
+
+ slides = ['']
+ line_count = 0
+
+ for line in lyrics.split('\n'):
+ line = line.strip()
+
+ if line == '':
+ # Empty line represents new slide
+ slides += ['']
+ line_count = 0
+
+ elif line_count < lines_per_slide:
+ # Add line to current slide
+ if line_count != 0: slides[-1] += '\n'
+ slides[-1] += line
+ line_count += 1
+
+ else:
+ # Overflow to new slide
+ slides += [line]
+ line_count = 1
+
+ # Remove first/last slide if empty
+ # len(slides) is always greater than 1 or single slide is not empty
+ if slides[0] == '': slides = slides[1:]
+ if slides[-1] == '': slides = slides[:-1]
+
+ return slides
diff --git a/tests/test_core.py b/tests/test_core.py
@@ -10,9 +10,9 @@ class TestUtils(unittest.TestCase):
# Mock os.getenv and requests.get
mocked_env.return_value = 'api://lyrics/{artist}/{title}'
- mocked_get.return_value.text = b'{"lyrics":"Test1\nTest2\nTest3\nTest4","title":"Foo","artist":"Bar","image":null}'
+ 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': 'Test1\nTest2\nTest3\nTest4',
+ 'lyrics': 'A\nB\nC\nD',
'title': 'Foo',
'artist': 'Bar',
}
@@ -25,7 +25,7 @@ class TestUtils(unittest.TestCase):
mocked_get.assert_called_with('api://lyrics/bar/foo')
self.assertEqual(song_data.title, 'Foo')
self.assertEqual(song_data.artist, 'Bar')
- self.assertEqual(song_data.lyrics, 'Test1\nTest2\nTest3\nTest4')
+ self.assertEqual(song_data.lyrics, 'A\nB\nC\nD')
def test_get_song_data_no_url(self):
with patch('songs2slides.utils.os.getenv') as mocked_env, \
@@ -58,3 +58,73 @@ class TestUtils(unittest.TestCase):
# Assert request was called
mocked_get.assert_called_with('api://lyrics/bar/foo')
+
+ def test_get_slide_contents_default(self):
+ # Declare song data and expected slides
+ lyrics = 'A\nB\nC\nD\nE\nF\n\nG\nH'
+ expected = ['A\nB\nC\nD', 'E\nF', 'G\nH']
+
+ # Get slide content
+ result = utils.get_slide_contents(lyrics)
+
+ self.assertEqual(result, expected)
+
+ def test_get_slide_contents_3_lines_per_slide(self):
+ # Declare song data and expected slides
+ lyrics = 'A\nB\nC\nD\nE\nF\n\nG\nH'
+ expected = ['A\nB\nC', 'D\nE\nF', 'G\nH']
+
+ # Get slide content
+ result = utils.get_slide_contents(lyrics, lines_per_slide = 3)
+
+ self.assertEqual(result, expected)
+
+ def test_get_slide_contents_empty_string(self):
+ # Declare song data and expected slides
+ lyrics = ''
+ expected = []
+
+ # Get slide content
+ result = utils.get_slide_contents(lyrics, lines_per_slide = 3)
+
+ self.assertEqual(result, expected)
+
+ def test_get_slide_contents_one_line(self):
+ # Declare song data and expected slides
+ lyrics = 'A'
+ expected = ['A']
+
+ # Get slide content
+ result = utils.get_slide_contents(lyrics)
+
+ self.assertEqual(result, expected)
+
+ def test_get_slide_contents_one_slide(self):
+ # Declare song data and expected slides
+ lyrics = 'A\nB\nC\nD'
+ expected = ['A\nB\nC\nD']
+
+ # Get slide content
+ result = utils.get_slide_contents(lyrics)
+
+ self.assertEqual(result, expected)
+
+ def test_get_slide_contents_tripple_newlines(self):
+ # Declare song data and expected slides
+ lyrics = 'A\nB\n\n\nC\nD'
+ expected = ['A\nB', '', 'C\nD']
+
+ # Get slide content
+ result = utils.get_slide_contents(lyrics)
+
+ self.assertEqual(result, expected)
+
+ def test_get_slide_contents_extra_whitespace(self):
+ # Declare song data and expected slides
+ lyrics = ' A\n B \nC D\nE '
+ expected = ['A\nB\nC D\nE']
+
+ # Get slide content
+ result = utils.get_slide_contents(lyrics)
+
+ self.assertEqual(result, expected)