songs2slides

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

commit eeb8a38289cbcd27f7b799bc24bacdcf5f640dc7
parent 2dab6949d1d70c6fb159cb2c4bafe85bdb1dcb7b
Author: ashermorgan <59518073+ashermorgan@users.noreply.github.com>
Date:   Wed, 28 Feb 2024 10:44:49 -0800

Implement online view feature

Diffstat:
Msongs2slides/routes.py | 14+++++++++-----
Msongs2slides/static/create.css | 8++++++++
Msongs2slides/static/create.js | 5++++-
Msongs2slides/templates/create-step-2.html | 12++++++++++++
Asongs2slides/templates/slides.html | 34++++++++++++++++++++++++++++++++++
Mtests/test_routes.py | 32++++++++++++++++++++++++++++++++
6 files changed, 99 insertions(+), 6 deletions(-)

diff --git a/songs2slides/routes.py b/songs2slides/routes.py @@ -71,8 +71,12 @@ def create_slides(): # Assemble slides slides = core.assemble_slides(songs, lines_per_slide = None) - # Create and send powerpoint - with tempfile.NamedTemporaryFile(suffix='.pptx') as f: - core.create_pptx(slides, f.name) - return send_file(f.name, as_attachment=True, - download_name='slides.pptx') + if (request.form.get('output-type') == 'pptx'): + # Create and send powerpoint + with tempfile.NamedTemporaryFile(suffix='.pptx') as f: + core.create_pptx(slides, f.name) + return send_file(f.name, as_attachment=True, + download_name='slides.pptx') + else: + # Render HTML slides + return render_template('slides.html', slides=slides) diff --git a/songs2slides/static/create.css b/songs2slides/static/create.css @@ -73,3 +73,11 @@ textarea { float: right; font-weight: bold; } + +fieldset { + border: none; +} +fieldset label { + display: block; + margin-top: 0.5rem; +} diff --git a/songs2slides/static/create.js b/songs2slides/static/create.js @@ -1,5 +1,8 @@ addEventListener("submit", () => { - document.getElementById('after-submit').hidden = false + if (document.querySelector('input[value=html]:checked]') === null) { + // Don't show #after-submit for HTML slide creation + document.getElementById('after-submit').hidden = false + } if (document.getElementById('step-2')) { document.getElementById('step-2').hidden = true } diff --git a/songs2slides/templates/create-step-2.html b/songs2slides/templates/create-step-2.html @@ -53,6 +53,18 @@ {% 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 id="actions"> <a href="{{ url_for('.create') }}">Back</a> <button> diff --git a/songs2slides/templates/slides.html b/songs2slides/templates/slides.html @@ -0,0 +1,34 @@ +<!DOCTYPE html> +<html> + <head> + <title>Online View - SongsSlides</title> + + <link rel="stylesheet" href="https://unpkg.com/reveal.js@^4//dist/reveal.css"> + <link rel="stylesheet" href="https://unpkg.com/reveal.js@^4//dist/theme/black.css"> + <script src="https://unpkg.com/reveal.js@^4//dist/reveal.js"></script> + </head> + + <body> + <div class="reveal"> + <div class="slides"> + {% for slide in slides %} + <section> + {% for line in slide.split('\n') %} + <p>{{ line }}</p> + {% endfor %} + </section> + {% endfor %} + </div> + </div> + + <script> + Reveal.initialize({ + controls: false, + help: false, + overview: false, + progress: false, + transition: 'none', + }); + </script> + </body> +</html> diff --git a/tests/test_routes.py b/tests/test_routes.py @@ -89,6 +89,7 @@ class TestRoutes(unittest.TestCase): 'title-2': 'T2', 'artist-2': 'A2', 'lyrics-2': 'L2', + 'output-type': 'pptx', }) # Assert mocks called correctly @@ -112,6 +113,7 @@ class TestRoutes(unittest.TestCase): 'lyrics-1': 'L1', 'title-2': 'T2', 'lyrics-2': 'L2', + 'output-type': 'pptx', }) # Assert response has 400 status code @@ -119,3 +121,33 @@ class TestRoutes(unittest.TestCase): # Assert assemble_slides not called mocked_assemble.assert_not_called() + + def test_create_slides_html_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', + }) + + # Assert mocks called correctly + mocked_assemble.assert_called_with([ + core.SongData('T1', 'A1', 'L1'), + core.SongData('T2', 'A2', 'L2'), + ], + lines_per_slide = None + ) + mocked_create.assert_not_called() + mocked_render.assert_called_with('slides.html', slides=slides)