songs2slides

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

commit eb2f67bd77645358a8ce5c2477fc687b1d30bf02
parent a1c5038d223fb2f05e6681001e00f7251e2662d5
Author: Asher Morgan <59518073+ashermorgan@users.noreply.github.com>
Date:   Tue, 26 Mar 2024 12:12:10 -0700

Implement title slide and blank slide settings

Diffstat:
Msongs2slides/routes.py | 5++++-
Msongs2slides/static/create.css | 3++-
Msongs2slides/templates/create.html | 35++++++++++++++++++++++++-----------
Mtests/test_routes.py | 80+++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++--
4 files changed, 108 insertions(+), 15 deletions(-)

diff --git a/songs2slides/routes.py b/songs2slides/routes.py @@ -67,9 +67,12 @@ def get_lyrics(): def create_slides(): # Parse form data songs = parse_form(request.form) + title_slides = 'title-slides' in request.form + blank_slides = 'blank-slides' in request.form # Assemble slides - slides = core.assemble_slides(songs, lines_per_slide = None) + slides = core.assemble_slides(songs, lines_per_slide = None, + title_slides=title_slides, blank_slides=blank_slides) if (request.form.get('output-type') == 'pptx'): # Create and send powerpoint diff --git a/songs2slides/static/create.css b/songs2slides/static/create.css @@ -76,8 +76,9 @@ textarea { fieldset { border: none; + margin-top: 1rem; } -fieldset label { +label { display: block; margin-top: 0.5rem; } diff --git a/songs2slides/templates/create.html b/songs2slides/templates/create.html @@ -144,17 +144,30 @@ {% endfor %} </div> - <fieldset> - <legend>Output type:</legend> - <label> - <input type="radio" name="output-type" value="pptx" checked/> - PowerPoint Download - </label> - <label> - <input type="radio" name="output-type" value="html"/> - Online View - </label> - </fieldset> + <div> + <fieldset> + <legend>Extra slides:</legend> + <label> + <input type="checkbox" name="title-slides" checked/> + Include a title slide before each song + </label> + <label> + <input type="checkbox" name="blank-slides" checked/> + Include a blank slide between each song + </label> + </fieldset> + <fieldset> + <legend>Output type:</legend> + <label> + <input type="radio" name="output-type" value="pptx" checked/> + PowerPoint Download + </label> + <label> + <input type="radio" name="output-type" value="html"/> + Online View + </label> + </fieldset> + </div> <div id="actions"> <input onclick="back()" type="button" value="Back"/> diff --git a/tests/test_routes.py b/tests/test_routes.py @@ -90,6 +90,8 @@ class TestRoutes(unittest.TestCase): 'artist-2': 'A2', 'lyrics-2': 'L2', 'output-type': 'pptx', + 'title-slides': 'on', + 'blank-slides': 'on', }) # Assert mocks called correctly @@ -97,7 +99,9 @@ class TestRoutes(unittest.TestCase): core.SongData('T1', 'A1', 'L1'), core.SongData('T2', 'A2', 'L2'), ], - lines_per_slide = None + lines_per_slide = None, + title_slides = True, + blank_slides = True, ) file = mocked_create.call_args.args[1] mocked_send.assert_called_with(file, as_attachment=True, @@ -114,6 +118,8 @@ class TestRoutes(unittest.TestCase): 'title-2': 'T2', 'lyrics-2': 'L2', 'output-type': 'pptx', + 'title-slides': 'on', + 'blank-slides': 'on', }) # Assert response has 400 status code @@ -140,6 +146,8 @@ class TestRoutes(unittest.TestCase): 'artist-2': 'A2', 'lyrics-2': 'L2', 'output-type': 'html', + 'title-slides': 'on', + 'blank-slides': 'on', }) # Assert mocks called correctly @@ -147,7 +155,75 @@ class TestRoutes(unittest.TestCase): core.SongData('T1', 'A1', 'L1'), core.SongData('T2', 'A2', 'L2'), ], - lines_per_slide = None + lines_per_slide = None, + title_slides = True, + blank_slides = True, + ) + mocked_create.assert_not_called() + mocked_render.assert_called_with('slides.html', slides=slides) + + def test_create_slides_no_title_slides(self): + with patch('songs2slides.core.assemble_slides') as mocked_assemble, \ + patch('songs2slides.core.create_pptx') as mocked_create, \ + patch('songs2slides.routes.render_template') as mocked_render: + + # Mock assemble_slides + slides = ['T1', 'L1\nL2', 'L3', 'T2', 'L4'] + mocked_assemble.return_value = slides + + # Send request + self.client.post('/slides/', data={ + 'title-1': 'T1', + 'artist-1': 'A1', + 'lyrics-1': 'L1', + 'title-2': 'T2', + 'artist-2': 'A2', + 'lyrics-2': 'L2', + 'output-type': 'html', + 'blank-slides': 'on', + }) + + # Assert mocks called correctly + mocked_assemble.assert_called_with([ + core.SongData('T1', 'A1', 'L1'), + core.SongData('T2', 'A2', 'L2'), + ], + lines_per_slide = None, + title_slides = False, + blank_slides = True, + ) + mocked_create.assert_not_called() + mocked_render.assert_called_with('slides.html', slides=slides) + + def test_create_slides_no_blank_slides(self): + with patch('songs2slides.core.assemble_slides') as mocked_assemble, \ + patch('songs2slides.core.create_pptx') as mocked_create, \ + patch('songs2slides.routes.render_template') as mocked_render: + + # Mock assemble_slides + slides = ['T1', 'L1\nL2', 'L3', 'T2', 'L4'] + mocked_assemble.return_value = slides + + # Send request + self.client.post('/slides/', data={ + 'title-1': 'T1', + 'artist-1': 'A1', + 'lyrics-1': 'L1', + 'title-2': 'T2', + 'artist-2': 'A2', + 'lyrics-2': 'L2', + 'output-type': 'html', + 'title-slides': 'on', + }) + + # Assert mocks called correctly + mocked_assemble.assert_called_with([ + core.SongData('T1', 'A1', 'L1'), + core.SongData('T2', 'A2', 'L2'), + ], + lines_per_slide = None, + title_slides = True, + blank_slides = False, ) mocked_create.assert_not_called() mocked_render.assert_called_with('slides.html', slides=slides)