diff --git a/backend/app.py b/backend/app.py index 263dbb4..2a201f4 100644 --- a/backend/app.py +++ b/backend/app.py @@ -7,6 +7,7 @@ import os import json from flask_basicauth import BasicAuth from helpers import nocache +from werkzeug.utils import secure_filename app = Flask(__name__, static_url_path='/static') basic_auth = BasicAuth(app) @@ -170,6 +171,70 @@ def query_songs_with_details(input_string=""): return jsonify(result) +@app.route("/api/songs/suggest") +@nocache +def query_songs_with_details_suggest(input_string=""): + input_string = request.args.get("count", input_string) + if input_string == "": + return Response(status=400) + result = [] + if not input_string.isnumeric(): + return Response(status=400) + count: int = int(input_string) + for x in database.get_song_suggestions(count): + # Turn row into dict. Add field labels. + result.append(dict(zip(['karafun_id', 'title', 'artist', 'year', 'duo', 'explicit', 'styles', 'languages'], x))) + return jsonify(result) + + +@app.route("/api/songs/stats") +@nocache +# Return the data from long_term_stats as json +def get_stats(): + db_result = database.get_long_term_stats() + data = [] + for row in db_result: + data.append(dict(zip(['id', 'count'], row))) + return jsonify(data) + + +@app.route("/api/songs/stats.csv") +@nocache +# Return data from long_term_stats as csv +def get_stats_csv(): + db_result = database.get_long_term_stats() + print(db_result) + csv = "Id,Playbacks\n" + for row in db_result: + csv += str(row[0]) + "," + str(row[1]) + "\n" + return Response(csv, mimetype='text/csv') + + +@app.route("/api/songs/stats.csv", methods=['POST']) +@nocache +@basic_auth.required +# Update long_term_stats from csv +def update_stats_csv(): + if not request.files: + abort(400) + file = request.files['file'] + if file.filename is None: + abort(400) + else: + filename = secure_filename(file.filename) + if filename == '': + abort(400) + if not filename.endswith('.csv'): + abort(400) + if file: + if database.import_stats(file): + return Response('{"status": "OK"}', mimetype='text/json') + else: + return Response('{"status": "FAIL"}', mimetype='text/json', status=400) + else: + abort(400) + + @app.route("/api/songs/details/") def get_song_details(song_id): result = database.get_song_details(song_id) @@ -261,14 +326,20 @@ def get_accept_entries(): return Response('{"status": "OK", "value": ' + str(int(accept_entries)) + '}', mimetype='text/json') -@app.route("/api/played/clear") +@app.route("/api/event/close") @nocache @basic_auth.required -def clear_played_songs(): - if database.clear_played_songs(): +def close_event(): + try: + database.transfer_playbacks() + database.clear_played_songs() + database.delete_all_entries() + helpers.reset_current_event_id(app) return Response('{"status": "OK"}', mimetype='text/json') - else: - return Response('{"status": "FAIL"}', mimetype='text/json') + except Exception: + response = jsonify({"status": "FAIL", "message": "An error occured while closing the event."}) + response.status_code = 400 + return response @app.route("/api/entries/delete_all") @@ -298,6 +369,7 @@ def activate_job(): with app.app_context(): helpers.load_dbconfig(app) helpers.load_version(app) + database.create_schema() database.create_entry_table() database.create_song_table() database.create_done_song_table() diff --git a/backend/database.py b/backend/database.py index d5f90f8..6d18d84 100644 --- a/backend/database.py +++ b/backend/database.py @@ -40,6 +40,31 @@ def import_songs(song_csv): return ("Imported songs ({} in Database)".format(num_songs)) +def import_stats(stats_csv): + print("Start importing Stats...") + df = pandas.read_csv(stats_csv, sep=',') + if (df.columns[0] != "Id" or df.columns[1] != "Playbacks"): + return False + with get_db_engine().connect() as conn: + for index, row in df.iterrows(): + stmt = text( + "INSERT INTO long_term_stats (Id,Playbacks) VALUES (:par_id,:par_playbacks) ON DUPLICATE KEY UPDATE Playbacks=:par_playbacks") + conn.execute(stmt, {"par_id": row["Id"], "par_playbacks": row["Playbacks"]}) + conn.commit() + return True + + +def create_schema(): + create_song_table() + create_entry_table() + create_done_song_table() + create_config_table() + create_long_term_stats_table() + create_list_view() + create_done_song_view() + init_event_id() + + def create_entry_table(): with get_db_engine().connect() as conn: stmt = text( @@ -75,6 +100,17 @@ def create_song_table(): conn.commit() +def create_long_term_stats_table(): + with get_db_engine().connect() as conn: + stmt = text("""CREATE TABLE IF NOT EXISTS `long_term_stats` ( + `Id` INTEGER, + `Playbacks` INTEGER, + PRIMARY KEY (`Id`) + )""") + conn.execute(stmt) + conn.commit() + + def create_list_view(): with get_db_engine().connect() as conn: stmt = text("""CREATE OR REPLACE VIEW `Liste` AS @@ -121,6 +157,34 @@ def get_played_list(): return cur.fetchall() +def get_song_suggestions(count: int): + with get_db_engine().connect() as conn: + # Get the top 10 songs with the most plays from the long_term_stats table and join them with the songs table to get the song details. + # Exclude songs that are already in the queue, or in the done_songs table. + stmt = text(""" + SELECT s.Id, s.Title, s.Artist, s.Year, s.Duo, s.Explicit, s.Styles, s.Languages + FROM long_term_stats lts + LEFT JOIN songs s ON lts.Id = s.Id + LEFT JOIN entries e ON lts.Id = e.Song_Id + LEFT JOIN done_songs ds ON lts.Id = ds.Song_Id + WHERE e.Id IS NULL AND ds.Song_Id IS NULL + ORDER BY lts.Playbacks DESC + LIMIT :count; + """) + cur = conn.execute(stmt, {"count": count}) + return cur.fetchall() + + +def get_long_term_stats(): + with get_db_engine().connect() as conn: + stmt = text(""" + SELECT lts.Id, lts.Playbacks + FROM long_term_stats lts + """) + cur = conn.execute(stmt) + return cur.fetchall() + + def get_song_list(): with get_db_engine().connect() as conn: stmt = text("SELECT Artist || \" - \" || Title AS Song, Id FROM songs;") @@ -224,6 +288,23 @@ def check_queue_length(): return cur.fetchall()[0][0] +def transfer_playbacks(): + with get_db_engine().connect() as conn: + # Use SQL to update the long_term_stats table. Add the playbacks of the songs in the done_songs table to the playbacks of the songs in the long_term_stats table. + stmt = text(""" + INSERT INTO long_term_stats(Id, Playbacks) + SELECT ds.Song_Id, ds.Plays + FROM done_songs ds + LEFT JOIN long_term_stats lts ON ds.Song_Id = lts.Id + ON DUPLICATE KEY + UPDATE Playbacks = lts.Playbacks + VALUES(Playbacks); + """) + result = conn.execute(stmt) + print(result) + conn.commit() + return True + + def clear_played_songs(): with get_db_engine().connect() as conn: conn.execute(text("DELETE FROM done_songs")) diff --git a/backend/templates/played_list.html b/backend/templates/played_list.html index 8c4f7e3..79b3842 100644 --- a/backend/templates/played_list.html +++ b/backend/templates/played_list.html @@ -2,8 +2,8 @@ {% block title %}Abspielliste{% endblock %} {% block content %}
- +