songs2slides

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

commit 73c2865f715bbc12ce377bb451e0906717627bdd
parent 42ddf7cfd1f3b6a7547dc0ebc3484970b6ad8d20
Author: ashermorgan <59518073+ashermorgan@users.noreply.github.com>
Date:   Sat, 24 Feb 2024 17:14:00 -0800

Implement /slides/ route

Diffstat:
Msongs2slides/__init__.py | 4++--
Msongs2slides/routes.py | 53+++++++++++++++++++++++++++++++++++++++++++++++++++--
Atests/test_routes.py | 53+++++++++++++++++++++++++++++++++++++++++++++++++++++
3 files changed, 106 insertions(+), 4 deletions(-)

diff --git a/songs2slides/__init__.py b/songs2slides/__init__.py @@ -3,7 +3,7 @@ from flask import Flask def create_app(): app = Flask(__name__) - with app.app_context(): - from songs2slides import routes + from . import routes + app.register_blueprint(routes.bp) return app diff --git a/songs2slides/routes.py b/songs2slides/routes.py @@ -1,5 +1,54 @@ -from flask import current_app as app +from flask import abort, Blueprint, request, send_file +import tempfile -@app.route('/') +from songs2slides import core + +bp = Blueprint('main', __name__) + +def parse_form(form): + """ + Parse song data from a form + + Parameters + ---------- + form : flask.Request.form + The form data + + Returns + ------- + list of core.SongData + The parsed song information + """ + + songs = [] + try: + i = 1 + while f'title-{i}' in request.form: + songs += [core.SongData( + form[f'title-{i}'], + form[f'artist-{i}'], + form.get(f'lyrics-{i}', None) + )] + i += 1 + except: + abort(400) + else: + return songs + +@bp.route('/') def index(): return '<p>Hello world</p>' + +@bp.post('/slides/') +def slides(): + # Parse form data + songs = parse_form(request.form) + + # 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') diff --git a/tests/test_routes.py b/tests/test_routes.py @@ -0,0 +1,53 @@ +import unittest +from unittest.mock import patch, call + +from songs2slides import create_app, core + +class TestRoutes(unittest.TestCase): + def setUp(self): + self.app = create_app() + self.client = self.app.test_client() + + def test_slides_basic(self): + with patch('songs2slides.core.assemble_slides') as mocked_assemble, \ + patch('songs2slides.core.create_pptx') as mocked_create, \ + patch('songs2slides.routes.send_file') as mocked_send: + + # 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', + }) + + # Assert mocks called correctly + mocked_assemble.assert_called_with([ + core.SongData('T1', 'A1', 'L1'), + core.SongData('T2', 'A2', 'L2'), + ], + lines_per_slide = None + ) + file = mocked_create.call_args.args[1] + mocked_send.assert_called_with(file, as_attachment=True, + download_name='slides.pptx') + + def test_slides_mising_artist(self): + with patch('songs2slides.core.assemble_slides') as mocked_assemble: + + # Send request + res = self.client.post('/slides/', data={ + 'title-1': 'T1', + 'artist-1': 'A1', + 'lyrics-1': 'L1', + 'title-2': 'T2', + 'lyrics-2': 'L2', + }) + + # Assert response has 400 status code + self.assertEqual(res.status_code, 400) + + # Assert assemble_slides not called + mocked_assemble.assert_not_called()