From faad60346fe3ffc70ffaf1f3b468684cc1e35899 Mon Sep 17 00:00:00 2001
From: =?UTF-8?q?Phillip=20K=C3=BChne?=
Date: Fri, 15 Jul 2022 14:00:37 +0200
Subject: [PATCH 01/85] Intial changes for MariaDB
---
backend/Dockerfile => Dockerfile | 0
backend/app.py | 6 +-
backend/database.py | 139 +++++++++++++++----------------
backend/requirements.txt | 5 +-
docker-compose.prod.yml | 11 +++
5 files changed, 83 insertions(+), 78 deletions(-)
rename backend/Dockerfile => Dockerfile (100%)
create mode 100644 docker-compose.prod.yml
diff --git a/backend/Dockerfile b/Dockerfile
similarity index 100%
rename from backend/Dockerfile
rename to Dockerfile
diff --git a/backend/app.py b/backend/app.py
index a36000c..7511e4e 100644
--- a/backend/app.py
+++ b/backend/app.py
@@ -1,4 +1,4 @@
-from flask import Flask, render_template, Response, abort, request, redirect, send_from_directory
+from flask import Flask, render_template, Response, abort, request, redirect, send_from_directory, jsonify
import helpers
import database
import data_adapters
@@ -131,8 +131,8 @@ def get_song_completions(input_string=""):
input_string = request.args.get('search', input_string)
if input_string != "":
print(input_string)
- list = database.get_song_completions(input_string=input_string)
- return Response(json.dumps(list, ensure_ascii=False).encode('utf-8'), mimetype='text/json')
+ result = [list(x) for x in database.get_song_completions(input_string=input_string)]
+ return jsonify(result)
else:
return 400
diff --git a/backend/database.py b/backend/database.py
index 8fe2cd2..f34d63e 100644
--- a/backend/database.py
+++ b/backend/database.py
@@ -1,6 +1,10 @@
# -*- coding: utf_8 -*-
+from types import NoneType
+from MySQLdb import Connection
+from sqlalchemy import create_engine
import sqlite3
+import mariadb
import pandas
from io import StringIO
@@ -9,23 +13,28 @@ entry_table = "entries"
index_label = "Id"
done_table = "done_songs"
+connection = None
-def open_db():
- conn = sqlite3.connect("/tmp/karaoqueue.db")
- conn.execute('PRAGMA encoding = "UTF-8";')
- return conn
+
+def open_db() -> Connection:
+ global connection
+ if (not connection):
+ engine = create_engine(
+ "mysql://ek0ur6p6ky9gdmif:jk571ov6g38g5iqv@eporqep6b4b8ql12.chr7pe7iynqr.eu-west-1.rds.amazonaws.com:3306/xdfmpudc3remzgj0")
+ connection = engine.connect()
+ # cur.execute('PRAGMA encoding = "UTF-8";')
+ return connection
def import_songs(song_csv):
print("Start importing Songs...")
df = pandas.read_csv(StringIO(song_csv), sep=';')
conn = open_db()
- cur = conn.cursor()
df.to_sql(song_table, conn, if_exists='replace',
index=False)
- cur.execute("SELECT Count(Id) FROM songs")
+ cur = conn.execute("SELECT Count(Id) FROM songs")
num_songs = cur.fetchone()[0]
- conn.close()
+ # conn.close()
print("Imported songs ({} in Database)".format(num_songs))
return("Imported songs ({} in Database)".format(num_songs))
@@ -34,157 +43,143 @@ def create_entry_table():
conn = open_db()
conn.execute('CREATE TABLE IF NOT EXISTS '+entry_table +
' (ID INTEGER PRIMARY KEY NOT NULL, Song_Id INTEGER NOT NULL, Name VARCHAR(255), Client_Id VARCHAR(36), Transferred INTEGER DEFAULT 0)')
- conn.close()
+ # conn.close()
def create_done_song_table():
conn = open_db()
conn.execute('CREATE TABLE IF NOT EXISTS '+done_table +
' (Song_Id INTEGER PRIMARY KEY NOT NULL, Plays INTEGER)')
- conn.close()
+ # conn.close()
def create_song_table():
conn = open_db()
- conn.execute("CREATE TABLE IF NOT EXISTS \""+song_table+"""\" (
- "Id" INTEGER,
- "Title" TEXT,
- "Artist" TEXT,
- "Year" INTEGER,
- "Duo" INTEGER,
- "Explicit" INTEGER,
- "Date Added" TEXT,
- "Styles" TEXT,
- "Languages" TEXT
+ conn.execute("CREATE TABLE IF NOT EXISTS `"+song_table+"""` (
+ `Id` INTEGER,
+ `Title` TEXT,
+ `Artist` TEXT,
+ `Year` INTEGER,
+ `Duo` INTEGER,
+ `Explicit` INTEGER,
+ `Date Added` TEXT,
+ `Styles` TEXT,
+ `Languages` TEXT
)""")
- conn.close()
+ # conn.close()
def create_list_view():
conn = open_db()
- conn.execute("""CREATE VIEW IF NOT EXISTS [Liste] AS
- SELECT Name, Title, Artist, entries.Id, songs.Id, entries.Transferred
+ conn.execute("""CREATE OR REPLACE VIEW `Liste` AS
+ SELECT Name, Title, Artist, entries.Id AS entry_ID, songs.Id AS song_ID, entries.Transferred
FROM entries, songs
- WHERE entries.Song_Id=songs.Id""")
- conn.close()
+ WHERE entries.Song_Id=Song_ID""")
+ # conn.close()
def create_done_song_view():
conn = open_db()
- conn.execute("""CREATE VIEW IF NOT EXISTS [Abspielliste] AS
+ conn.execute("""CREATE OR REPLACE VIEW `Abspielliste` AS
SELECT Artist || \" - \" || Title AS Song, Plays AS Wiedergaben
FROM songs, done_songs
WHERE done_songs.Song_Id=songs.Id""")
- conn.close()
+ # conn.close()
def get_list():
conn = open_db()
- conn.row_factory = sqlite3.Row
- cur = conn.cursor()
- cur.execute("SELECT * FROM Liste")
+ cur = conn.execute("SELECT * FROM Liste")
return cur.fetchall()
def get_played_list():
conn = open_db()
- cur = conn.cursor()
- cur.execute("SELECT * FROM Abspielliste")
+ cur = conn.execute("SELECT * FROM Abspielliste")
return cur.fetchall()
def get_song_list():
conn = open_db()
- cur = conn.cursor()
- cur.execute("SELECT Artist || \" - \" || Title AS Song, Id FROM songs;")
+ cur = conn.execute(
+ "SELECT Artist || \" - \" || Title AS Song, Id FROM songs;")
return cur.fetchall()
def get_song_completions(input_string):
conn = open_db()
- cur = conn.cursor()
# Don't look, it burns...
prepared_string = "%{0}%".format(
input_string).upper() # "Test" -> "%TEST%"
print(prepared_string)
- cur.execute(
- "SELECT Title || \" - \" || Artist AS Song, Id FROM songs WHERE REPLACE(REPLACE(REPLACE(REPLACE(UPPER( SONG ),'ö','Ö'),'ü','Ü'),'ä','Ä'),'ß','ẞ') LIKE (?) LIMIT 20;", (prepared_string,))
+ cur = conn.execute(
+ "SELECT CONCAT(Artist,\" - \",Title) AS Song, Id FROM songs WHERE CONCAT(Artist,\" - \",Title) LIKE (%s) LIMIT 20;", [prepared_string])
return cur.fetchall()
def add_entry(name, song_id, client_id):
conn = open_db()
- cur = conn.cursor()
- cur.execute(
- "INSERT INTO entries (Song_Id,Name,Client_Id) VALUES(?,?,?);", (song_id, name, client_id))
- conn.commit()
- conn.close()
+ conn.execute(
+ "INSERT INTO entries (Song_Id,Name,Client_Id) VALUES(%s,%s,%s);", (song_id, name, client_id))
+ # conn.close()
return
def add_sung_song(entry_id):
conn = open_db()
- cur = conn.cursor()
- cur.execute("""SELECT Song_Id FROM entries WHERE Id=?""", (entry_id,))
+ cur = conn.execute(
+ """SELECT Song_Id FROM entries WHERE Id=?""", (entry_id,))
song_id = cur.fetchone()[0]
- cur.execute("""INSERT OR REPLACE INTO done_songs (Song_Id, Plays)
+ conn.execute("""INSERT OR REPLACE INTO done_songs (Song_Id, Plays)
VALUES("""+str(song_id)+""",
COALESCE(
(SELECT Plays FROM done_songs
WHERE Song_Id="""+str(song_id)+"), 0) + 1)"
- )
- conn.commit()
+ )
delete_entry(entry_id)
- conn.close()
+ # conn.close()
return True
def toggle_transferred(entry_id):
conn = open_db()
- cur = conn.cursor()
- cur.execute("SELECT Transferred FROM entries WHERE ID =?", (entry_id,))
+ cur = conn.execute(
+ "SELECT Transferred FROM entries WHERE ID =?", (entry_id,))
marked = cur.fetchall()[0][0]
if(marked == 0):
- cur.execute(
+ conn.execute(
"UPDATE entries SET Transferred = 1 WHERE ID =?", (entry_id,))
else:
- cur.execute(
+ conn.execute(
"UPDATE entries SET Transferred = 0 WHERE ID =?", (entry_id,))
- conn.commit()
- conn.close()
+ # conn.close()
return True
def check_entry_quota(client_id):
conn = open_db()
- cur = conn.cursor()
- cur.execute(
+ cur = conn.execute(
"SELECT Count(*) FROM entries WHERE entries.Client_Id = ?", (client_id,))
return cur.fetchall()[0][0]
def check_queue_length():
conn = open_db()
- cur = conn.cursor()
- cur.execute("SELECT Count(*) FROM entries")
+ cur = conn.execute("SELECT Count(*) FROM entries")
return cur.fetchall()[0][0]
def clear_played_songs():
conn = open_db()
- cur = conn.cursor()
- cur.execute("DELETE FROM done_songs")
- conn.commit()
- conn.close()
+ conn.execute("DELETE FROM done_songs")
+ # conn.close()
return True
def delete_entry(id):
conn = open_db()
- cur = conn.cursor()
- cur.execute("DELETE FROM entries WHERE id=?", (id,))
- conn.commit()
- conn.close()
+ conn.execute("DELETE FROM entries WHERE id=?", (id,))
+ # conn.close()
return True
@@ -194,19 +189,15 @@ def delete_entries(ids):
idlist.append((x,))
try:
conn = open_db()
- cur = conn.cursor()
- cur.executemany("DELETE FROM entries WHERE id=?", idlist)
- conn.commit()
- conn.close()
+ cur = conn.execute("DELETE FROM entries WHERE id=?", idlist)
+ # conn.close()
return cur.rowcount
- except sqlite3.Error as error:
+ except mariadb.Error as error:
return -1
def delete_all_entries():
conn = open_db()
- cur = conn.cursor()
- cur.execute("DELETE FROM entries")
- conn.commit()
- conn.close()
+ conn.execute("DELETE FROM entries")
+ # conn.close()
return True
diff --git a/backend/requirements.txt b/backend/requirements.txt
index 04324d8..adb164c 100644
--- a/backend/requirements.txt
+++ b/backend/requirements.txt
@@ -2,4 +2,7 @@ requests
pandas
Flask-BasicAuth
bs4
-gunicorn
\ No newline at end of file
+gunicorn
+mariadb
+SQLAlchemy
+mysqlclient
\ No newline at end of file
diff --git a/docker-compose.prod.yml b/docker-compose.prod.yml
new file mode 100644
index 0000000..7ef04ae
--- /dev/null
+++ b/docker-compose.prod.yml
@@ -0,0 +1,11 @@
+version: "3.9"
+services:
+ db:
+ image: mariadb
+ restart: always
+ environment:
+ MARIADB_ROOT_PASSWORD: dpMAZj*Mc4%FZM!V
+ MARIADB_ROOT_HOST: localhost
+ MARIADB_DATABASE: karaoqueue
+ MARIADB_USER: karaoqueue
+ MARIADB_PASSWORD: a5G@P*^tCW$$w@wE
\ No newline at end of file
From 97dd80b03a255093ae4d0a1bcf3002676575d110 Mon Sep 17 00:00:00 2001
From: =?UTF-8?q?Phillip=20K=C3=BChne?=
Date: Fri, 15 Jul 2022 14:16:06 +0200
Subject: [PATCH 02/85] Add or update the Azure App Service build and
deployment workflow config
---
...ure-legacy-mariadb_database_karaoqueue.yml | 63 +++++++++++++++++++
1 file changed, 63 insertions(+)
create mode 100644 .github/workflows/feature-legacy-mariadb_database_karaoqueue.yml
diff --git a/.github/workflows/feature-legacy-mariadb_database_karaoqueue.yml b/.github/workflows/feature-legacy-mariadb_database_karaoqueue.yml
new file mode 100644
index 0000000..85f3dbf
--- /dev/null
+++ b/.github/workflows/feature-legacy-mariadb_database_karaoqueue.yml
@@ -0,0 +1,63 @@
+# Docs for the Azure Web Apps Deploy action: https://github.com/Azure/webapps-deploy
+# More GitHub Actions for Azure: https://github.com/Azure/actions
+# More info on Python, GitHub Actions, and Azure App Service: https://aka.ms/python-webapps-actions
+
+name: Build and deploy Python app to Azure Web App - karaoqueue
+
+on:
+ push:
+ branches:
+ - feature/legacy/mariadb_database
+ workflow_dispatch:
+
+jobs:
+ build:
+ runs-on: ubuntu-latest
+
+ steps:
+ - uses: actions/checkout@v2
+
+ - name: Set up Python version
+ uses: actions/setup-python@v1
+ with:
+ python-version: '3.9'
+
+ - name: Create and start virtual environment
+ run: |
+ python -m venv venv
+ source venv/bin/activate
+
+ - name: Install dependencies
+ run: pip install -r requirements.txt
+
+ # Optional: Add step to run tests here (PyTest, Django test suites, etc.)
+
+ - name: Upload artifact for deployment jobs
+ uses: actions/upload-artifact@v2
+ with:
+ name: python-app
+ path: |
+ .
+ !venv/
+
+ deploy:
+ runs-on: ubuntu-latest
+ needs: build
+ environment:
+ name: 'Production'
+ url: ${{ steps.deploy-to-webapp.outputs.webapp-url }}
+
+ steps:
+ - name: Download artifact from build job
+ uses: actions/download-artifact@v2
+ with:
+ name: python-app
+ path: .
+
+ - name: 'Deploy to Azure Web App'
+ uses: azure/webapps-deploy@v2
+ id: deploy-to-webapp
+ with:
+ app-name: 'karaoqueue'
+ slot-name: 'Production'
+ publish-profile: ${{ secrets.AZUREAPPSERVICE_PUBLISHPROFILE_8EBA2D69F5B7469D9CFD1F71F6A66108 }}
From fb9677dc8806a4a605aca29871aa133df65879d2 Mon Sep 17 00:00:00 2001
From: =?UTF-8?q?Phillip=20K=C3=BChne?=
Date: Fri, 15 Jul 2022 14:23:17 +0200
Subject: [PATCH 03/85] Fix github workflow
---
.../workflows/feature-legacy-mariadb_database_karaoqueue.yml | 4 ++--
1 file changed, 2 insertions(+), 2 deletions(-)
diff --git a/.github/workflows/feature-legacy-mariadb_database_karaoqueue.yml b/.github/workflows/feature-legacy-mariadb_database_karaoqueue.yml
index 85f3dbf..a8028a2 100644
--- a/.github/workflows/feature-legacy-mariadb_database_karaoqueue.yml
+++ b/.github/workflows/feature-legacy-mariadb_database_karaoqueue.yml
@@ -28,7 +28,7 @@ jobs:
source venv/bin/activate
- name: Install dependencies
- run: pip install -r requirements.txt
+ run: pip install -r backend/requirements.txt
# Optional: Add step to run tests here (PyTest, Django test suites, etc.)
@@ -37,7 +37,7 @@ jobs:
with:
name: python-app
path: |
- .
+ backend
!venv/
deploy:
From 3527ba4fd9cbdc518ba81ff61151ce32aed1fbe7 Mon Sep 17 00:00:00 2001
From: =?UTF-8?q?Phillip=20K=C3=BChne?=
Date: Fri, 15 Jul 2022 14:27:39 +0200
Subject: [PATCH 04/85] Fix dependencies
---
.../workflows/feature-legacy-mariadb_database_karaoqueue.yml | 4 ++++
1 file changed, 4 insertions(+)
diff --git a/.github/workflows/feature-legacy-mariadb_database_karaoqueue.yml b/.github/workflows/feature-legacy-mariadb_database_karaoqueue.yml
index a8028a2..96bbb70 100644
--- a/.github/workflows/feature-legacy-mariadb_database_karaoqueue.yml
+++ b/.github/workflows/feature-legacy-mariadb_database_karaoqueue.yml
@@ -27,6 +27,10 @@ jobs:
python -m venv venv
source venv/bin/activate
+ - name: Install mysql
+ run: sudo apt-get install -y mysql-server libmysqlclient-dev libmariadbclient-dev
+
+
- name: Install dependencies
run: pip install -r backend/requirements.txt
From c9ad755a04383c1db1a5f7eff4de39eb30811260 Mon Sep 17 00:00:00 2001
From: =?UTF-8?q?Phillip=20K=C3=BChne?=
Date: Fri, 15 Jul 2022 14:29:18 +0200
Subject: [PATCH 05/85] Fix?
---
.../workflows/feature-legacy-mariadb_database_karaoqueue.yml | 2 +-
1 file changed, 1 insertion(+), 1 deletion(-)
diff --git a/.github/workflows/feature-legacy-mariadb_database_karaoqueue.yml b/.github/workflows/feature-legacy-mariadb_database_karaoqueue.yml
index 96bbb70..317b045 100644
--- a/.github/workflows/feature-legacy-mariadb_database_karaoqueue.yml
+++ b/.github/workflows/feature-legacy-mariadb_database_karaoqueue.yml
@@ -28,7 +28,7 @@ jobs:
source venv/bin/activate
- name: Install mysql
- run: sudo apt-get install -y mysql-server libmysqlclient-dev libmariadbclient-dev
+ run: sudo apt-get install -y mariadb-client
- name: Install dependencies
From b88aac69e63e23955edccef994876da438f9554a Mon Sep 17 00:00:00 2001
From: =?UTF-8?q?Phillip=20K=C3=BChne?=
Date: Fri, 15 Jul 2022 14:32:06 +0200
Subject: [PATCH 06/85] Fix..
---
.../workflows/feature-legacy-mariadb_database_karaoqueue.yml | 2 +-
1 file changed, 1 insertion(+), 1 deletion(-)
diff --git a/.github/workflows/feature-legacy-mariadb_database_karaoqueue.yml b/.github/workflows/feature-legacy-mariadb_database_karaoqueue.yml
index 317b045..5e5798f 100644
--- a/.github/workflows/feature-legacy-mariadb_database_karaoqueue.yml
+++ b/.github/workflows/feature-legacy-mariadb_database_karaoqueue.yml
@@ -28,7 +28,7 @@ jobs:
source venv/bin/activate
- name: Install mysql
- run: sudo apt-get install -y mariadb-client
+ run: sudo sudo aptitude install -y mariadb-server
- name: Install dependencies
From 2a92c394508e7795d410b8818b5f3796fa45517d Mon Sep 17 00:00:00 2001
From: =?UTF-8?q?Phillip=20K=C3=BChne?=
Date: Fri, 15 Jul 2022 14:32:51 +0200
Subject: [PATCH 07/85] Test
---
.../workflows/feature-legacy-mariadb_database_karaoqueue.yml | 2 +-
1 file changed, 1 insertion(+), 1 deletion(-)
diff --git a/.github/workflows/feature-legacy-mariadb_database_karaoqueue.yml b/.github/workflows/feature-legacy-mariadb_database_karaoqueue.yml
index 5e5798f..3938261 100644
--- a/.github/workflows/feature-legacy-mariadb_database_karaoqueue.yml
+++ b/.github/workflows/feature-legacy-mariadb_database_karaoqueue.yml
@@ -28,7 +28,7 @@ jobs:
source venv/bin/activate
- name: Install mysql
- run: sudo sudo aptitude install -y mariadb-server
+ run: sudo sudo apt install -y mariadb-server
- name: Install dependencies
From 8151e615c0d716de991e31a4d06148a9ace5584a Mon Sep 17 00:00:00 2001
From: =?UTF-8?q?Phillip=20K=C3=BChne?=
Date: Fri, 15 Jul 2022 14:34:07 +0200
Subject: [PATCH 08/85] Test
---
.../workflows/feature-legacy-mariadb_database_karaoqueue.yml | 2 +-
1 file changed, 1 insertion(+), 1 deletion(-)
diff --git a/.github/workflows/feature-legacy-mariadb_database_karaoqueue.yml b/.github/workflows/feature-legacy-mariadb_database_karaoqueue.yml
index 3938261..38da577 100644
--- a/.github/workflows/feature-legacy-mariadb_database_karaoqueue.yml
+++ b/.github/workflows/feature-legacy-mariadb_database_karaoqueue.yml
@@ -28,7 +28,7 @@ jobs:
source venv/bin/activate
- name: Install mysql
- run: sudo sudo apt install -y mariadb-server
+ run: sudo sudo apt install -y mysql-server
- name: Install dependencies
From 69e252e28a32b4453c29b4c7fd4b6e0cba4edb78 Mon Sep 17 00:00:00 2001
From: =?UTF-8?q?Phillip=20K=C3=BChne?=
Date: Fri, 15 Jul 2022 14:35:52 +0200
Subject: [PATCH 09/85] Add missing libraries to install
---
.../workflows/feature-legacy-mariadb_database_karaoqueue.yml | 2 +-
1 file changed, 1 insertion(+), 1 deletion(-)
diff --git a/.github/workflows/feature-legacy-mariadb_database_karaoqueue.yml b/.github/workflows/feature-legacy-mariadb_database_karaoqueue.yml
index 38da577..d81fb22 100644
--- a/.github/workflows/feature-legacy-mariadb_database_karaoqueue.yml
+++ b/.github/workflows/feature-legacy-mariadb_database_karaoqueue.yml
@@ -28,7 +28,7 @@ jobs:
source venv/bin/activate
- name: Install mysql
- run: sudo sudo apt install -y mysql-server
+ run: sudo sudo apt install -y mysql-server libmysqlclient-dev libmariadbclient-dev
- name: Install dependencies
From 2a18f3dc2c1cca744dea981d28f72adbdc3dc3a2 Mon Sep 17 00:00:00 2001
From: =?UTF-8?q?Phillip=20K=C3=BChne?=
Date: Fri, 15 Jul 2022 14:36:47 +0200
Subject: [PATCH 10/85] Completely remove mariadb libs to hopefully fix
conflicts
---
.../workflows/feature-legacy-mariadb_database_karaoqueue.yml | 2 +-
1 file changed, 1 insertion(+), 1 deletion(-)
diff --git a/.github/workflows/feature-legacy-mariadb_database_karaoqueue.yml b/.github/workflows/feature-legacy-mariadb_database_karaoqueue.yml
index d81fb22..9f8f323 100644
--- a/.github/workflows/feature-legacy-mariadb_database_karaoqueue.yml
+++ b/.github/workflows/feature-legacy-mariadb_database_karaoqueue.yml
@@ -28,7 +28,7 @@ jobs:
source venv/bin/activate
- name: Install mysql
- run: sudo sudo apt install -y mysql-server libmysqlclient-dev libmariadbclient-dev
+ run: sudo sudo apt install -y mysql-server libmysqlclient-dev
- name: Install dependencies
From 9907a19066320c90fc8f6aefb9d37781b9a28adb Mon Sep 17 00:00:00 2001
From: =?UTF-8?q?Phillip=20K=C3=BChne?=
Date: Fri, 15 Jul 2022 14:43:16 +0200
Subject: [PATCH 11/85] Test
---
.../workflows/feature-legacy-mariadb_database_karaoqueue.yml | 2 +-
1 file changed, 1 insertion(+), 1 deletion(-)
diff --git a/.github/workflows/feature-legacy-mariadb_database_karaoqueue.yml b/.github/workflows/feature-legacy-mariadb_database_karaoqueue.yml
index 9f8f323..4a6170f 100644
--- a/.github/workflows/feature-legacy-mariadb_database_karaoqueue.yml
+++ b/.github/workflows/feature-legacy-mariadb_database_karaoqueue.yml
@@ -28,7 +28,7 @@ jobs:
source venv/bin/activate
- name: Install mysql
- run: sudo sudo apt install -y mysql-server libmysqlclient-dev
+ run: sudo sudo apt install -y mysql-server libmysqlclient-dev python-dev
- name: Install dependencies
From 2138189dff5d94d75c16dc55b4f3e3157d0b7ee9 Mon Sep 17 00:00:00 2001
From: =?UTF-8?q?Phillip=20K=C3=BChne?=
Date: Fri, 15 Jul 2022 14:44:26 +0200
Subject: [PATCH 12/85] python3 is still not default on Ubuntu?
---
.../workflows/feature-legacy-mariadb_database_karaoqueue.yml | 2 +-
1 file changed, 1 insertion(+), 1 deletion(-)
diff --git a/.github/workflows/feature-legacy-mariadb_database_karaoqueue.yml b/.github/workflows/feature-legacy-mariadb_database_karaoqueue.yml
index 4a6170f..2675db4 100644
--- a/.github/workflows/feature-legacy-mariadb_database_karaoqueue.yml
+++ b/.github/workflows/feature-legacy-mariadb_database_karaoqueue.yml
@@ -28,7 +28,7 @@ jobs:
source venv/bin/activate
- name: Install mysql
- run: sudo sudo apt install -y mysql-server libmysqlclient-dev python-dev
+ run: sudo sudo apt install -y mysql-server libmysqlclient-dev python3-dev
- name: Install dependencies
From 3ae2803fccc2086502772d184b45a460713af4d3 Mon Sep 17 00:00:00 2001
From: =?UTF-8?q?Phillip=20K=C3=BChne?=
Date: Fri, 15 Jul 2022 14:51:05 +0200
Subject: [PATCH 13/85] Remove mysql and install mariadb
---
.../feature-legacy-mariadb_database_karaoqueue.yml | 6 ++++--
1 file changed, 4 insertions(+), 2 deletions(-)
diff --git a/.github/workflows/feature-legacy-mariadb_database_karaoqueue.yml b/.github/workflows/feature-legacy-mariadb_database_karaoqueue.yml
index 2675db4..4c4640a 100644
--- a/.github/workflows/feature-legacy-mariadb_database_karaoqueue.yml
+++ b/.github/workflows/feature-legacy-mariadb_database_karaoqueue.yml
@@ -27,8 +27,10 @@ jobs:
python -m venv venv
source venv/bin/activate
- - name: Install mysql
- run: sudo sudo apt install -y mysql-server libmysqlclient-dev python3-dev
+ - name: Install mariadb
+ run: |
+ sudo apt-get remove --purge mysql-server mysql-client mysql-common libmysqlclient-dev
+ sudo sudo apt install -y libmariadbclient-dev python3-dev
- name: Install dependencies
From 67a9552fee06adbde680373bd02e0a3d11591e65 Mon Sep 17 00:00:00 2001
From: =?UTF-8?q?Phillip=20K=C3=BChne?=
Date: Fri, 15 Jul 2022 14:55:14 +0200
Subject: [PATCH 14/85] Update ubuntu to get newer mariadb connector
---
.../workflows/feature-legacy-mariadb_database_karaoqueue.yml | 2 +-
1 file changed, 1 insertion(+), 1 deletion(-)
diff --git a/.github/workflows/feature-legacy-mariadb_database_karaoqueue.yml b/.github/workflows/feature-legacy-mariadb_database_karaoqueue.yml
index 4c4640a..da661e3 100644
--- a/.github/workflows/feature-legacy-mariadb_database_karaoqueue.yml
+++ b/.github/workflows/feature-legacy-mariadb_database_karaoqueue.yml
@@ -12,7 +12,7 @@ on:
jobs:
build:
- runs-on: ubuntu-latest
+ runs-on: ubuntu-22.04
steps:
- uses: actions/checkout@v2
From adfd120636dbef8743449c936c0bb7de991ea706 Mon Sep 17 00:00:00 2001
From: =?UTF-8?q?Phillip=20K=C3=BChne?=
Date: Fri, 15 Jul 2022 15:01:07 +0200
Subject: [PATCH 15/85] Update package names for 22.04
---
.../workflows/feature-legacy-mariadb_database_karaoqueue.yml | 2 +-
1 file changed, 1 insertion(+), 1 deletion(-)
diff --git a/.github/workflows/feature-legacy-mariadb_database_karaoqueue.yml b/.github/workflows/feature-legacy-mariadb_database_karaoqueue.yml
index da661e3..3c0e492 100644
--- a/.github/workflows/feature-legacy-mariadb_database_karaoqueue.yml
+++ b/.github/workflows/feature-legacy-mariadb_database_karaoqueue.yml
@@ -30,7 +30,7 @@ jobs:
- name: Install mariadb
run: |
sudo apt-get remove --purge mysql-server mysql-client mysql-common libmysqlclient-dev
- sudo sudo apt install -y libmariadbclient-dev python3-dev
+ sudo sudo apt install -y libmariadbd-dev python3-dev
- name: Install dependencies
From fc78bdc4feaf7821d6649ae0db694e3184ad9ebe Mon Sep 17 00:00:00 2001
From: =?UTF-8?q?Phillip=20K=C3=BChne?=
Date: Fri, 15 Jul 2022 15:09:48 +0200
Subject: [PATCH 16/85] Fix types
---
backend/database.py | 7 +++----
1 file changed, 3 insertions(+), 4 deletions(-)
diff --git a/backend/database.py b/backend/database.py
index f34d63e..c7bdc48 100644
--- a/backend/database.py
+++ b/backend/database.py
@@ -1,9 +1,8 @@
# -*- coding: utf_8 -*-
-from types import NoneType
+from email.mime import base
from MySQLdb import Connection
-from sqlalchemy import create_engine
-import sqlite3
+from sqlalchemy import create_engine, engine
import mariadb
import pandas
from io import StringIO
@@ -16,7 +15,7 @@ done_table = "done_songs"
connection = None
-def open_db() -> Connection:
+def open_db() -> engine.base.Connection:
global connection
if (not connection):
engine = create_engine(
From 57ced69dddb72b46e61d97374240382209b3dc1a Mon Sep 17 00:00:00 2001
From: =?UTF-8?q?Phillip=20K=C3=BChne?=
Date: Fri, 15 Jul 2022 15:27:36 +0200
Subject: [PATCH 17/85] Load DB Connection from ENV
---
backend/app.py | 1 +
backend/database.py | 4 ++--
backend/helpers.py | 13 +++++++++++++
3 files changed, 16 insertions(+), 2 deletions(-)
diff --git a/backend/app.py b/backend/app.py
index 7511e4e..0a86157 100644
--- a/backend/app.py
+++ b/backend/app.py
@@ -229,6 +229,7 @@ def admin():
@app.before_first_request
def activate_job():
+ helpers.load_dbconfig(app)
helpers.load_version(app)
helpers.create_data_directory()
database.create_entry_table()
diff --git a/backend/database.py b/backend/database.py
index c7bdc48..7ba70c7 100644
--- a/backend/database.py
+++ b/backend/database.py
@@ -6,6 +6,7 @@ from sqlalchemy import create_engine, engine
import mariadb
import pandas
from io import StringIO
+from flask import current_app
song_table = "songs"
entry_table = "entries"
@@ -18,8 +19,7 @@ connection = None
def open_db() -> engine.base.Connection:
global connection
if (not connection):
- engine = create_engine(
- "mysql://ek0ur6p6ky9gdmif:jk571ov6g38g5iqv@eporqep6b4b8ql12.chr7pe7iynqr.eu-west-1.rds.amazonaws.com:3306/xdfmpudc3remzgj0")
+ engine = create_engine(current_app.config.get("DBCONNSTRING"))
connection = engine.connect()
# cur.execute('PRAGMA encoding = "UTF-8";')
return connection
diff --git a/backend/helpers.py b/backend/helpers.py
index 17545a6..c937f04 100644
--- a/backend/helpers.py
+++ b/backend/helpers.py
@@ -47,6 +47,19 @@ def load_version(app):
app.config['VERSION'] = ""
else:
app.config['VERSION'] = ""
+
+def load_dbconfig(app):
+ if os.environ.get("JAWSDB_MARIA_URL"):
+ app.config['VERSION'] = os.environ.get("JAWSDB_MARIA_URL")
+ elif os.path.isfile(".dbconn"):
+ with open('.dbconn', 'r') as file:
+ data = file.read().replace('\n', '')
+ if data:
+ app.config['DBCONNSTRING'] = data
+ else:
+ app.config['DBCONNSTRING'] = ""
+ else:
+ app.config['DBCONNSTRING'] = ""
def setup_config(app):
if check_config_exists():
From 72914109c054653965c8abcd1402a76509a83478 Mon Sep 17 00:00:00 2001
From: =?UTF-8?q?Phillip=20K=C3=BChne?=
Date: Fri, 15 Jul 2022 15:33:42 +0200
Subject: [PATCH 18/85] prune requirements
---
backend/database.py | 3 +--
backend/requirements.txt | 1 -
2 files changed, 1 insertion(+), 3 deletions(-)
diff --git a/backend/database.py b/backend/database.py
index 7ba70c7..528221d 100644
--- a/backend/database.py
+++ b/backend/database.py
@@ -3,7 +3,6 @@
from email.mime import base
from MySQLdb import Connection
from sqlalchemy import create_engine, engine
-import mariadb
import pandas
from io import StringIO
from flask import current_app
@@ -191,7 +190,7 @@ def delete_entries(ids):
cur = conn.execute("DELETE FROM entries WHERE id=?", idlist)
# conn.close()
return cur.rowcount
- except mariadb.Error as error:
+ except Exception as error:
return -1
diff --git a/backend/requirements.txt b/backend/requirements.txt
index adb164c..d816517 100644
--- a/backend/requirements.txt
+++ b/backend/requirements.txt
@@ -3,6 +3,5 @@ pandas
Flask-BasicAuth
bs4
gunicorn
-mariadb
SQLAlchemy
mysqlclient
\ No newline at end of file
From bc44985fed2a34b3435409f322ca73a22ca1ae76 Mon Sep 17 00:00:00 2001
From: =?UTF-8?q?Phillip=20K=C3=BChne?=
Date: Fri, 15 Jul 2022 15:35:58 +0200
Subject: [PATCH 19/85] Fix DBCONNSTRING
---
backend/database.py | 1 +
1 file changed, 1 insertion(+)
diff --git a/backend/database.py b/backend/database.py
index 528221d..579f141 100644
--- a/backend/database.py
+++ b/backend/database.py
@@ -18,6 +18,7 @@ connection = None
def open_db() -> engine.base.Connection:
global connection
if (not connection):
+ print(current_app.config.get("DBCONNSTRING"))
engine = create_engine(current_app.config.get("DBCONNSTRING"))
connection = engine.connect()
# cur.execute('PRAGMA encoding = "UTF-8";')
From a22960eae92713d744e76ae2f6693a87cf3ebdac Mon Sep 17 00:00:00 2001
From: =?UTF-8?q?Phillip=20K=C3=BChne?=
Date: Fri, 15 Jul 2022 15:54:15 +0200
Subject: [PATCH 20/85] Fix DB Connection
---
.vscode/launch.json | 3 ++-
backend/helpers.py | 24 +++++++++++++++---------
2 files changed, 17 insertions(+), 10 deletions(-)
diff --git a/.vscode/launch.json b/.vscode/launch.json
index 2e00725..d9b4ed5 100644
--- a/.vscode/launch.json
+++ b/.vscode/launch.json
@@ -14,7 +14,8 @@
"env": {
"FLASK_APP": "backend/app.py",
"FLASK_ENV": "development",
- "FLASK_DEBUG": "1"
+ "FLASK_DEBUG": "1",
+ "DBSTRING": "mysql://ek0ur6p6ky9gdmif:kpmi2bav4mvh4jbx@eporqep6b4b8ql12.chr7pe7iynqr.eu-west-1.rds.amazonaws.com:3306/xdfmpudc3remzgj0"
},
"args": [
"run",
diff --git a/backend/helpers.py b/backend/helpers.py
index c937f04..53ee6a1 100644
--- a/backend/helpers.py
+++ b/backend/helpers.py
@@ -49,17 +49,23 @@ def load_version(app):
app.config['VERSION'] = ""
def load_dbconfig(app):
- if os.environ.get("JAWSDB_MARIA_URL"):
- app.config['VERSION'] = os.environ.get("JAWSDB_MARIA_URL")
- elif os.path.isfile(".dbconn"):
- with open('.dbconn', 'r') as file:
- data = file.read().replace('\n', '')
- if data:
- app.config['DBCONNSTRING'] = data
+ if os.environ.get("FLASK_ENV") == "development":
+ app.config['DBCONNSTRING'] = os.environ.get("DBSTRING")
+ else:
+ if os.environ.get("DEPLOYMENT_PLATFORM") == "Heroku":
+ if os.environ.get("JAWSDB_MARIA_URL"):
+ app.config['DBCONNSTRING'] = os.environ.get("JAWSDB_MARIA_URL")
else:
app.config['DBCONNSTRING'] = ""
- else:
- app.config['DBCONNSTRING'] = ""
+ elif os.path.isfile(".dbconn"):
+ with open('.dbconn', 'r') as file:
+ data = file.read().replace('\n', '')
+ if data:
+ app.config['DBCONNSTRING'] = data
+ else:
+ app.config['DBCONNSTRING'] = ""
+ else:
+ app.config['DBCONNSTRING'] = ""
def setup_config(app):
if check_config_exists():
From 8dd90b728cd59f330f971c251664be634b2e879e Mon Sep 17 00:00:00 2001
From: =?UTF-8?q?Phillip=20K=C3=BChne?=
Date: Sat, 8 Oct 2022 19:47:29 +0200
Subject: [PATCH 21/85] MySQL Working, bug fixes.
---
.vscode/launch.json | 10 ++++----
.vscode/tasks.json | 7 ++++++
Dockerfile | 2 +-
backend/app.py | 13 +++++-----
backend/database.py | 40 ++++++++++++++++--------------
backend/helpers.py | 41 ++++++++++++++++++++-----------
backend/templates/base.html | 2 +-
backend/templates/main_admin.html | 10 +++++---
docker-compose.prod.yml | 12 +++++++++
9 files changed, 86 insertions(+), 51 deletions(-)
diff --git a/.vscode/launch.json b/.vscode/launch.json
index d9b4ed5..27002f1 100644
--- a/.vscode/launch.json
+++ b/.vscode/launch.json
@@ -5,7 +5,7 @@
"version": "0.2.0",
"configurations": [
{
- "preLaunchTask": "versiondump",
+ "preLaunchTask": "mariadb",
"name": "Python: Flask",
"type": "python",
"cwd": "${workspaceFolder}/backend",
@@ -15,7 +15,7 @@
"FLASK_APP": "backend/app.py",
"FLASK_ENV": "development",
"FLASK_DEBUG": "1",
- "DBSTRING": "mysql://ek0ur6p6ky9gdmif:kpmi2bav4mvh4jbx@eporqep6b4b8ql12.chr7pe7iynqr.eu-west-1.rds.amazonaws.com:3306/xdfmpudc3remzgj0"
+ "DBSTRING": "mysql://devuser:devpw@127.0.0.1:3306/karaoqueue"
},
"args": [
"run",
@@ -25,7 +25,7 @@
"jinja": true
},
{
- "preLaunchTask": "versiondump",
+ "preLaunchTask": "mariadb",
"name": "Python: Flask (with reload)",
"type": "python",
"cwd": "${workspaceFolder}/backend",
@@ -43,7 +43,7 @@
"jinja": true
},
{
- "preLaunchTask": "versiondump",
+ "preLaunchTask": "mariadb",
"name": "Python: Flask (with reload, externally reachable)",
"type": "python",
"cwd": "${workspaceFolder}/backend",
@@ -62,7 +62,7 @@
"jinja": true
},
{
- "preLaunchTask": "versiondump",
+ "preLaunchTask": "mariadb",
"name": "Python: Flask (externally reachable)",
"type": "python",
"cwd": "${workspaceFolder}/backend",
diff --git a/.vscode/tasks.json b/.vscode/tasks.json
index 563a855..bdaee63 100644
--- a/.vscode/tasks.json
+++ b/.vscode/tasks.json
@@ -8,6 +8,13 @@
"type": "shell",
"command": "echo \"$(git rev-parse --abbrev-ref HEAD)-$(git describe)\"> ${workspaceFolder}/backend/.version",
"problemMatcher": []
+ },
+ {
+ "label": "mariadb",
+ "type": "shell",
+ "command": "docker run --rm --name some-mariadb --env MARIADB_USER=devuser --env MARIADB_PASSWORD=devpw --env MARIADB_ROOT_PASSWORD=devrootpw --env MARIADB_DATABASE=karaoqueue -p 3306:3306 mariadb:latest",
+ "isBackground": true,
+ "activeOnStart": false
}
]
}
\ No newline at end of file
diff --git a/Dockerfile b/Dockerfile
index 0e36a09..7389e11 100644
--- a/Dockerfile
+++ b/Dockerfile
@@ -8,4 +8,4 @@ RUN pip install Flask-BasicAuth
RUN pip install bs4
-COPY ./app /app
\ No newline at end of file
+COPY ./backend /app
\ No newline at end of file
diff --git a/backend/app.py b/backend/app.py
index 0a86157..370c2b6 100644
--- a/backend/app.py
+++ b/backend/app.py
@@ -1,4 +1,5 @@
-from flask import Flask, render_template, Response, abort, request, redirect, send_from_directory, jsonify
+from flask import Flask, render_template, abort, request, redirect, send_from_directory, jsonify
+from flask.wrappers import Request, Response
import helpers
import database
import data_adapters
@@ -9,7 +10,7 @@ from helpers import nocache
app = Flask(__name__, static_url_path='/static')
basic_auth = BasicAuth(app)
-accept_entries = False
+accept_entries = True
@app.route("/")
def home():
@@ -81,12 +82,12 @@ def settings():
def settings_post():
entryquota = request.form.get("entryquota")
maxqueue = request.form.get("maxqueue")
- if entryquota.isnumeric() and int(entryquota) > 0:
- app.config['ENTRY_QUOTA'] = int(entryquota)
+ if entryquota.isnumeric() and int(entryquota) > 0: # type: ignore
+ app.config['ENTRY_QUOTA'] = int(entryquota) # type: ignore
else:
abort(400)
- if maxqueue.isnumeric and int(maxqueue) > 0:
- app.config['MAX_QUEUE'] = int(maxqueue)
+ if maxqueue.isnumeric and int(maxqueue) > 0: # type: ignore
+ app.config['MAX_QUEUE'] = int(maxqueue) # type: ignore
else:
abort(400)
diff --git a/backend/database.py b/backend/database.py
index 579f141..fe9805d 100644
--- a/backend/database.py
+++ b/backend/database.py
@@ -19,7 +19,7 @@ def open_db() -> engine.base.Connection:
global connection
if (not connection):
print(current_app.config.get("DBCONNSTRING"))
- engine = create_engine(current_app.config.get("DBCONNSTRING"))
+ engine = create_engine(current_app.config.get("DBCONNSTRING")) # type: ignore
connection = engine.connect()
# cur.execute('PRAGMA encoding = "UTF-8";')
return connection
@@ -32,7 +32,7 @@ def import_songs(song_csv):
df.to_sql(song_table, conn, if_exists='replace',
index=False)
cur = conn.execute("SELECT Count(Id) FROM songs")
- num_songs = cur.fetchone()[0]
+ num_songs = cur.fetchone()[0] # type: ignore
# conn.close()
print("Imported songs ({} in Database)".format(num_songs))
return("Imported songs ({} in Database)".format(num_songs))
@@ -41,7 +41,7 @@ def import_songs(song_csv):
def create_entry_table():
conn = open_db()
conn.execute('CREATE TABLE IF NOT EXISTS '+entry_table +
- ' (ID INTEGER PRIMARY KEY NOT NULL, Song_Id INTEGER NOT NULL, Name VARCHAR(255), Client_Id VARCHAR(36), Transferred INTEGER DEFAULT 0)')
+ ' (ID INTEGER PRIMARY KEY NOT NULL AUTO_INCREMENT, Song_Id INTEGER NOT NULL, Name VARCHAR(255), Client_Id VARCHAR(36), Transferred INTEGER DEFAULT 0)')
# conn.close()
@@ -73,14 +73,14 @@ def create_list_view():
conn.execute("""CREATE OR REPLACE VIEW `Liste` AS
SELECT Name, Title, Artist, entries.Id AS entry_ID, songs.Id AS song_ID, entries.Transferred
FROM entries, songs
- WHERE entries.Song_Id=Song_ID""")
+ WHERE entries.Song_Id=songs.Id""")
# conn.close()
def create_done_song_view():
conn = open_db()
conn.execute("""CREATE OR REPLACE VIEW `Abspielliste` AS
- SELECT Artist || \" - \" || Title AS Song, Plays AS Wiedergaben
+ SELECT CONCAT(Artist," - ", Title) AS Song, Plays AS Wiedergaben
FROM songs, done_songs
WHERE done_songs.Song_Id=songs.Id""")
# conn.close()
@@ -127,14 +127,16 @@ def add_entry(name, song_id, client_id):
def add_sung_song(entry_id):
conn = open_db()
cur = conn.execute(
- """SELECT Song_Id FROM entries WHERE Id=?""", (entry_id,))
- song_id = cur.fetchone()[0]
- conn.execute("""INSERT OR REPLACE INTO done_songs (Song_Id, Plays)
- VALUES("""+str(song_id)+""",
- COALESCE(
- (SELECT Plays FROM done_songs
- WHERE Song_Id="""+str(song_id)+"), 0) + 1)"
- )
+ """SELECT Song_Id FROM entries WHERE Id=%s""", (entry_id,))
+ song_id = cur.fetchone()[0] # type: ignore
+ conn.execute("""INSERT INTO done_songs (Song_Id, Plays) VALUES("""+str(song_id)+""",1) ON DUPLICATE KEY UPDATE Plays=Plays + 1;""")
+# SQLite bullshittery
+# conn.execute("""REPLACE INTO done_songs (Song_Id, Plays)
+# VALUES("""+str(song_id)+""",
+# COALESCE(
+# (SELECT Plays FROM done_songs
+# WHERE Song_Id="""+str(song_id)+"), 0) + 1)"
+# )
delete_entry(entry_id)
# conn.close()
return True
@@ -143,14 +145,14 @@ def add_sung_song(entry_id):
def toggle_transferred(entry_id):
conn = open_db()
cur = conn.execute(
- "SELECT Transferred FROM entries WHERE ID =?", (entry_id,))
+ "SELECT Transferred FROM entries WHERE ID =%s", (entry_id,))
marked = cur.fetchall()[0][0]
if(marked == 0):
conn.execute(
- "UPDATE entries SET Transferred = 1 WHERE ID =?", (entry_id,))
+ "UPDATE entries SET Transferred = 1 WHERE ID =%s", (entry_id,))
else:
conn.execute(
- "UPDATE entries SET Transferred = 0 WHERE ID =?", (entry_id,))
+ "UPDATE entries SET Transferred = 0 WHERE ID =%s", (entry_id,))
# conn.close()
return True
@@ -158,7 +160,7 @@ def toggle_transferred(entry_id):
def check_entry_quota(client_id):
conn = open_db()
cur = conn.execute(
- "SELECT Count(*) FROM entries WHERE entries.Client_Id = ?", (client_id,))
+ "SELECT Count(*) FROM entries WHERE entries.Client_Id = %s", (client_id,))
return cur.fetchall()[0][0]
@@ -177,7 +179,7 @@ def clear_played_songs():
def delete_entry(id):
conn = open_db()
- conn.execute("DELETE FROM entries WHERE id=?", (id,))
+ conn.execute("DELETE FROM entries WHERE id=%s", (id,))
# conn.close()
return True
@@ -188,7 +190,7 @@ def delete_entries(ids):
idlist.append((x,))
try:
conn = open_db()
- cur = conn.execute("DELETE FROM entries WHERE id=?", idlist)
+ cur = conn.execute("DELETE FROM entries WHERE id=%s", idlist)
# conn.close()
return cur.rowcount
except Exception as error:
diff --git a/backend/helpers.py b/backend/helpers.py
index 53ee6a1..656a6bf 100644
--- a/backend/helpers.py
+++ b/backend/helpers.py
@@ -37,7 +37,7 @@ def check_config_exists():
def load_version(app):
if os.environ.get("SOURCE_VERSION"):
- app.config['VERSION'] = os.environ.get("SOURCE_VERSION")[0:7]
+ app.config['VERSION'] = os.environ.get("SOURCE_VERSION")[0:7] # type: ignore
elif os.path.isfile(".version"):
with open('.version', 'r') as file:
data = file.read().replace('\n', '')
@@ -57,6 +57,11 @@ def load_dbconfig(app):
app.config['DBCONNSTRING'] = os.environ.get("JAWSDB_MARIA_URL")
else:
app.config['DBCONNSTRING'] = ""
+ if os.environ.get("DEPLOYMENT_PLATFORM") == "Docker":
+ if os.environ.get("DBSTRING"):
+ app.config['DBCONNSTRING'] = os.environ.get("DBSTRING")
+ else:
+ app.config['DBCONNSTRING'] = ""
elif os.path.isfile(".dbconn"):
with open('.dbconn', 'r') as file:
data = file.read().replace('\n', '')
@@ -68,20 +73,26 @@ def load_dbconfig(app):
app.config['DBCONNSTRING'] = ""
def setup_config(app):
- if check_config_exists():
- config = json.load(open(config_file))
- with open(config_file, 'r') as handle:
- config = json.load(handle)
- print("Loaded existing config")
+ if os.environ.get("DEPLOYMENT_PLATFORM") == "Docker":
+ app.config['BASIC_AUTH_USERNAME'] = os.environ.get('BASIC_AUTH_USERNAME')
+ app.config['BASIC_AUTH_PASSWORD'] = os.environ.get('BASIC_AUTH_PASSWORD')
+ app.config['ENTRY_QUOTA'] = os.environ.get('ENTRY_QUOTA')
+ app.config['MAX_QUEUE'] = os.environ.get('MAX_QUEUE')
else:
- config = {'username': 'admin', 'password': 'changeme', 'entryquota': 3, 'maxqueue': 20}
- with open(config_file, 'w') as handle:
- json.dump(config, handle, indent=4, sort_keys=True)
- print("Wrote new config")
- app.config['BASIC_AUTH_USERNAME'] = config['username']
- app.config['BASIC_AUTH_PASSWORD'] = config['password']
- app.config['ENTRY_QUOTA'] = config['entryquota']
- app.config['MAX_QUEUE'] = config['maxqueue']
+ if check_config_exists():
+ config = json.load(open(config_file))
+ with open(config_file, 'r') as handle:
+ config = json.load(handle)
+ print("Loaded existing config")
+ else:
+ config = {'username': 'admin', 'password': 'changeme', 'entryquota': 3, 'maxqueue': 20}
+ with open(config_file, 'w') as handle:
+ json.dump(config, handle, indent=4, sort_keys=True)
+ print("Wrote new config")
+ app.config['BASIC_AUTH_USERNAME'] = config['username']
+ app.config['BASIC_AUTH_PASSWORD'] = config['password']
+ app.config['ENTRY_QUOTA'] = config['entryquota']
+ app.config['MAX_QUEUE'] = config['maxqueue']
@@ -89,7 +100,7 @@ def nocache(view):
@wraps(view)
def no_cache(*args, **kwargs):
response = make_response(view(*args, **kwargs))
- response.headers['Last-Modified'] = datetime.now()
+ response.headers['Last-Modified'] = datetime.now() # type: ignore
response.headers['Cache-Control'] = 'no-store, no-cache, must-revalidate, post-check=0, pre-check=0, max-age=0'
response.headers['Pragma'] = 'no-cache'
response.headers['Expires'] = '-1'
diff --git a/backend/templates/base.html b/backend/templates/base.html
index f243850..8b598ea 100644
--- a/backend/templates/base.html
+++ b/backend/templates/base.html
@@ -75,7 +75,7 @@
{% endif %}
- KaraoQueue {{karaoqueue_version}} - © 2019-21 - Phillip
+ KaraoQueue {{karaoqueue_version}} - 2019-22 - Phillip
Kühne
diff --git a/backend/templates/main_admin.html b/backend/templates/main_admin.html
index 5234a88..5379dc2 100644
--- a/backend/templates/main_admin.html
+++ b/backend/templates/main_admin.html
@@ -52,10 +52,10 @@ table td:nth-child(2) {
{% block extrajs %}
diff --git a/docker-compose.prod.yml b/docker-compose.prod.yml
index 7ef04ae..2ee48c0 100644
--- a/docker-compose.prod.yml
+++ b/docker-compose.prod.yml
@@ -1,5 +1,17 @@
version: "3.9"
services:
+ karaoqueue:
+ image: "phillipkhne/karaoqueue:latest"
+ restart: always
+ ports:
+ - "127.0.0.1:8081:80"
+ environment:
+ DEPLOYMENT_PLATFORM: Docker
+ DBSTRING: mysql://user:pass@host:3306/database
+ BASIC_AUTH_USERNAME: admin
+ BASIC_AUTH_PASSWORD: changeme
+ ENTRY_QUOTA: 3
+ MAX_QUEUE: 20
db:
image: mariadb
restart: always
From 698c3717fda1b0d638bf2cf0ab21358f1019753c Mon Sep 17 00:00:00 2001
From: =?UTF-8?q?Phillip=20K=C3=BChne?=
Date: Sat, 8 Oct 2022 19:50:35 +0200
Subject: [PATCH 22/85] Remove outdated workflows
---
...ure-legacy-mariadb_database_karaoqueue.yml | 69 -------------------
1 file changed, 69 deletions(-)
delete mode 100644 .github/workflows/feature-legacy-mariadb_database_karaoqueue.yml
diff --git a/.github/workflows/feature-legacy-mariadb_database_karaoqueue.yml b/.github/workflows/feature-legacy-mariadb_database_karaoqueue.yml
deleted file mode 100644
index 3c0e492..0000000
--- a/.github/workflows/feature-legacy-mariadb_database_karaoqueue.yml
+++ /dev/null
@@ -1,69 +0,0 @@
-# Docs for the Azure Web Apps Deploy action: https://github.com/Azure/webapps-deploy
-# More GitHub Actions for Azure: https://github.com/Azure/actions
-# More info on Python, GitHub Actions, and Azure App Service: https://aka.ms/python-webapps-actions
-
-name: Build and deploy Python app to Azure Web App - karaoqueue
-
-on:
- push:
- branches:
- - feature/legacy/mariadb_database
- workflow_dispatch:
-
-jobs:
- build:
- runs-on: ubuntu-22.04
-
- steps:
- - uses: actions/checkout@v2
-
- - name: Set up Python version
- uses: actions/setup-python@v1
- with:
- python-version: '3.9'
-
- - name: Create and start virtual environment
- run: |
- python -m venv venv
- source venv/bin/activate
-
- - name: Install mariadb
- run: |
- sudo apt-get remove --purge mysql-server mysql-client mysql-common libmysqlclient-dev
- sudo sudo apt install -y libmariadbd-dev python3-dev
-
-
- - name: Install dependencies
- run: pip install -r backend/requirements.txt
-
- # Optional: Add step to run tests here (PyTest, Django test suites, etc.)
-
- - name: Upload artifact for deployment jobs
- uses: actions/upload-artifact@v2
- with:
- name: python-app
- path: |
- backend
- !venv/
-
- deploy:
- runs-on: ubuntu-latest
- needs: build
- environment:
- name: 'Production'
- url: ${{ steps.deploy-to-webapp.outputs.webapp-url }}
-
- steps:
- - name: Download artifact from build job
- uses: actions/download-artifact@v2
- with:
- name: python-app
- path: .
-
- - name: 'Deploy to Azure Web App'
- uses: azure/webapps-deploy@v2
- id: deploy-to-webapp
- with:
- app-name: 'karaoqueue'
- slot-name: 'Production'
- publish-profile: ${{ secrets.AZUREAPPSERVICE_PUBLISHPROFILE_8EBA2D69F5B7469D9CFD1F71F6A66108 }}
From 41a24ad9cea35ca04ca62e2fc5d8849642c571b6 Mon Sep 17 00:00:00 2001
From: =?UTF-8?q?Phillip=20K=C3=BChne?=
Date: Mon, 27 Mar 2023 01:02:26 +0200
Subject: [PATCH 23/85] readd main.py
---
backend/{wsgi.py => main.py} | 0
1 file changed, 0 insertions(+), 0 deletions(-)
rename backend/{wsgi.py => main.py} (100%)
diff --git a/backend/wsgi.py b/backend/main.py
similarity index 100%
rename from backend/wsgi.py
rename to backend/main.py
From 831166f38bd9046e8e98b819a03cc08ade21cc2e Mon Sep 17 00:00:00 2001
From: =?UTF-8?q?Phillip=20K=C3=BChne?=
Date: Mon, 27 Mar 2023 23:38:22 +0200
Subject: [PATCH 24/85] Intall dependencies using requirements.txt
---
Dockerfile | 10 ++--------
1 file changed, 2 insertions(+), 8 deletions(-)
diff --git a/Dockerfile b/Dockerfile
index 7389e11..1dcac9d 100644
--- a/Dockerfile
+++ b/Dockerfile
@@ -1,11 +1,5 @@
FROM tiangolo/uwsgi-nginx-flask:python3.7
-RUN pip install requests
+COPY ./backend /app
-RUN pip install pandas
-
-RUN pip install Flask-BasicAuth
-
-RUN pip install bs4
-
-COPY ./backend /app
\ No newline at end of file
+RUN pip install -r /app/requirements.txt
\ No newline at end of file
From b8220732eed2ec229fc8d2628bdb6bb3bd9ed6eb Mon Sep 17 00:00:00 2001
From: =?UTF-8?q?Phillip=20K=C3=BChne?=
Date: Tue, 28 Mar 2023 00:54:04 +0200
Subject: [PATCH 25/85] Introduce "dev environment" and dotenv
There are now two dockerfiles. One for production, one for development.
All configuration is now also handled through dotenv files,
which these dotenv files, as well as the included VSCode launch tasks
use.
---
.env.dev | 14 ++++
.gitignore | 5 +-
.vscode/launch.json | 5 +-
.vscode/tasks.json | 2 +-
backend/database.py | 153 +++++++++++++++++-----------------------
docker-compose.prod.yml | 11 ++-
docker-compose.yml | 15 ++++
7 files changed, 112 insertions(+), 93 deletions(-)
create mode 100644 .env.dev
create mode 100644 docker-compose.yml
diff --git a/.env.dev b/.env.dev
new file mode 100644
index 0000000..1fa9576
--- /dev/null
+++ b/.env.dev
@@ -0,0 +1,14 @@
+# MariaDB
+MARIADB_ROOT_PASSWORD=mariadb_root_password
+MARIADB_ROOT_HOST=localhost
+MARIADB_DATABASE=karaoqueue
+MARIADB_USER=karaoqueue
+MARIADB_PASSWORD=mariadb_karaoqueue_password
+
+# Karaoqueue
+DEPLOYMENT_PLATFORM=Docker
+DBSTRING=mysql://karaoqueue:mariadb_karaoqueue_password@127.0.0.1:3306/karaoqueue
+BASIC_AUTH_USERNAME=admin
+BASIC_AUTH_PASSWORD=change_me
+ENTRY_QUOTA=3
+MAX_QUEUE=20
\ No newline at end of file
diff --git a/.gitignore b/.gitignore
index 9f3255d..184e737 100644
--- a/.gitignore
+++ b/.gitignore
@@ -137,4 +137,7 @@ data/
node_modules/
# Version identification file
-.version
\ No newline at end of file
+.version
+
+# Docker secrets
+secrets.yml
\ No newline at end of file
diff --git a/.vscode/launch.json b/.vscode/launch.json
index 27002f1..594135a 100644
--- a/.vscode/launch.json
+++ b/.vscode/launch.json
@@ -15,8 +15,8 @@
"FLASK_APP": "backend/app.py",
"FLASK_ENV": "development",
"FLASK_DEBUG": "1",
- "DBSTRING": "mysql://devuser:devpw@127.0.0.1:3306/karaoqueue"
},
+ "envFile": "${workspaceFolder}/.env.dev",
"args": [
"run",
"--no-debugger",
@@ -36,6 +36,7 @@
"FLASK_ENV": "development",
"FLASK_DEBUG": "1"
},
+ "envFile": "${workspaceFolder}/.env.dev",
"args": [
"run",
"--no-debugger"
@@ -54,6 +55,7 @@
"FLASK_ENV": "development",
"FLASK_DEBUG": "1"
},
+ "envFile": "${workspaceFolder}/.env.dev",
"args": [
"run",
"--no-debugger",
@@ -73,6 +75,7 @@
"FLASK_ENV": "development",
"FLASK_DEBUG": "1"
},
+ "envFile": "${workspaceFolder}/.env.dev",
"args": [
"run",
"--no-debugger",
diff --git a/.vscode/tasks.json b/.vscode/tasks.json
index bdaee63..96e7e93 100644
--- a/.vscode/tasks.json
+++ b/.vscode/tasks.json
@@ -12,7 +12,7 @@
{
"label": "mariadb",
"type": "shell",
- "command": "docker run --rm --name some-mariadb --env MARIADB_USER=devuser --env MARIADB_PASSWORD=devpw --env MARIADB_ROOT_PASSWORD=devrootpw --env MARIADB_DATABASE=karaoqueue -p 3306:3306 mariadb:latest",
+ "command": "docker-compose -f docker-compose.yml up --remove-orphans",
"isBackground": true,
"activeOnStart": false
}
diff --git a/backend/database.py b/backend/database.py
index fe9805d..a3e6d0b 100644
--- a/backend/database.py
+++ b/backend/database.py
@@ -12,49 +12,44 @@ entry_table = "entries"
index_label = "Id"
done_table = "done_songs"
-connection = None
+sql_engine = None
-def open_db() -> engine.base.Connection:
- global connection
- if (not connection):
+def get_db_engine() -> engine.base.Engine:
+ global sql_engine
+ if (not sql_engine):
print(current_app.config.get("DBCONNSTRING"))
- engine = create_engine(current_app.config.get("DBCONNSTRING")) # type: ignore
- connection = engine.connect()
- # cur.execute('PRAGMA encoding = "UTF-8";')
- return connection
+ sql_engine = create_engine(current_app.config.get("DBCONNSTRING")) # type: ignore
+ return sql_engine
def import_songs(song_csv):
print("Start importing Songs...")
df = pandas.read_csv(StringIO(song_csv), sep=';')
- conn = open_db()
- df.to_sql(song_table, conn, if_exists='replace',
- index=False)
- cur = conn.execute("SELECT Count(Id) FROM songs")
- num_songs = cur.fetchone()[0] # type: ignore
- # conn.close()
+ with get_db_engine().connect() as conn:
+ df.to_sql(song_table, conn, if_exists='replace',
+ index=False)
+ cur = conn.execute("SELECT Count(Id) FROM songs")
+ num_songs = cur.fetchone()[0] # type: ignore
print("Imported songs ({} in Database)".format(num_songs))
return("Imported songs ({} in Database)".format(num_songs))
def create_entry_table():
- conn = open_db()
- conn.execute('CREATE TABLE IF NOT EXISTS '+entry_table +
+ with get_db_engine().connect() as conn:
+ conn.execute('CREATE TABLE IF NOT EXISTS '+entry_table +
' (ID INTEGER PRIMARY KEY NOT NULL AUTO_INCREMENT, Song_Id INTEGER NOT NULL, Name VARCHAR(255), Client_Id VARCHAR(36), Transferred INTEGER DEFAULT 0)')
- # conn.close()
def create_done_song_table():
- conn = open_db()
- conn.execute('CREATE TABLE IF NOT EXISTS '+done_table +
+ with get_db_engine().connect() as conn:
+ conn.execute('CREATE TABLE IF NOT EXISTS '+done_table +
' (Song_Id INTEGER PRIMARY KEY NOT NULL, Plays INTEGER)')
- # conn.close()
def create_song_table():
- conn = open_db()
- conn.execute("CREATE TABLE IF NOT EXISTS `"+song_table+"""` (
+ with get_db_engine().connect() as conn:
+ conn.execute("CREATE TABLE IF NOT EXISTS `"+song_table+"""` (
`Id` INTEGER,
`Title` TEXT,
`Artist` TEXT,
@@ -65,122 +60,107 @@ def create_song_table():
`Styles` TEXT,
`Languages` TEXT
)""")
- # conn.close()
def create_list_view():
- conn = open_db()
- conn.execute("""CREATE OR REPLACE VIEW `Liste` AS
+ with get_db_engine().connect() as conn:
+ conn.execute("""CREATE OR REPLACE VIEW `Liste` AS
SELECT Name, Title, Artist, entries.Id AS entry_ID, songs.Id AS song_ID, entries.Transferred
FROM entries, songs
WHERE entries.Song_Id=songs.Id""")
- # conn.close()
def create_done_song_view():
- conn = open_db()
- conn.execute("""CREATE OR REPLACE VIEW `Abspielliste` AS
+ with get_db_engine().connect() as conn:
+ conn.execute("""CREATE OR REPLACE VIEW `Abspielliste` AS
SELECT CONCAT(Artist," - ", Title) AS Song, Plays AS Wiedergaben
FROM songs, done_songs
WHERE done_songs.Song_Id=songs.Id""")
- # conn.close()
def get_list():
- conn = open_db()
- cur = conn.execute("SELECT * FROM Liste")
+ with get_db_engine().connect() as conn:
+ cur = conn.execute("SELECT * FROM Liste")
return cur.fetchall()
def get_played_list():
- conn = open_db()
- cur = conn.execute("SELECT * FROM Abspielliste")
+ with get_db_engine().connect() as conn:
+ cur = conn.execute("SELECT * FROM Abspielliste")
return cur.fetchall()
def get_song_list():
- conn = open_db()
- cur = conn.execute(
+ with get_db_engine().connect() as conn:
+ cur = conn.execute(
"SELECT Artist || \" - \" || Title AS Song, Id FROM songs;")
return cur.fetchall()
def get_song_completions(input_string):
- conn = open_db()
- # Don't look, it burns...
- prepared_string = "%{0}%".format(
- input_string).upper() # "Test" -> "%TEST%"
- print(prepared_string)
- cur = conn.execute(
- "SELECT CONCAT(Artist,\" - \",Title) AS Song, Id FROM songs WHERE CONCAT(Artist,\" - \",Title) LIKE (%s) LIMIT 20;", [prepared_string])
+ with get_db_engine().connect() as conn:
+ # Don't look, it burns...
+ prepared_string = "%{0}%".format(
+ input_string).upper() # "Test" -> "%TEST%"
+ print(prepared_string)
+ cur = conn.execute(
+ "SELECT CONCAT(Artist,\" - \",Title) AS Song, Id FROM songs WHERE CONCAT(Artist,\" - \",Title) LIKE (%s) LIMIT 20;", [prepared_string])
return cur.fetchall()
def add_entry(name, song_id, client_id):
- conn = open_db()
- conn.execute(
+ with get_db_engine().connect() as conn:
+ conn.execute(
"INSERT INTO entries (Song_Id,Name,Client_Id) VALUES(%s,%s,%s);", (song_id, name, client_id))
- # conn.close()
return
def add_sung_song(entry_id):
- conn = open_db()
- cur = conn.execute(
- """SELECT Song_Id FROM entries WHERE Id=%s""", (entry_id,))
- song_id = cur.fetchone()[0] # type: ignore
- conn.execute("""INSERT INTO done_songs (Song_Id, Plays) VALUES("""+str(song_id)+""",1) ON DUPLICATE KEY UPDATE Plays=Plays + 1;""")
-# SQLite bullshittery
-# conn.execute("""REPLACE INTO done_songs (Song_Id, Plays)
-# VALUES("""+str(song_id)+""",
-# COALESCE(
-# (SELECT Plays FROM done_songs
-# WHERE Song_Id="""+str(song_id)+"), 0) + 1)"
-# )
- delete_entry(entry_id)
- # conn.close()
+ with get_db_engine().connect() as conn:
+ cur = conn.execute(
+ """SELECT Song_Id FROM entries WHERE Id=%s""", (entry_id,))
+ song_id = cur.fetchone()[0] # type: ignore
+ conn.execute("""INSERT INTO done_songs (Song_Id, Plays) VALUES("""+str(song_id)+""",1) ON DUPLICATE KEY UPDATE Plays=Plays + 1;""")
+ delete_entry(entry_id)
return True
def toggle_transferred(entry_id):
- conn = open_db()
- cur = conn.execute(
- "SELECT Transferred FROM entries WHERE ID =%s", (entry_id,))
- marked = cur.fetchall()[0][0]
- if(marked == 0):
- conn.execute(
- "UPDATE entries SET Transferred = 1 WHERE ID =%s", (entry_id,))
- else:
- conn.execute(
- "UPDATE entries SET Transferred = 0 WHERE ID =%s", (entry_id,))
- # conn.close()
+ with get_db_engine().connect() as conn:
+ cur = conn.execute(
+ "SELECT Transferred FROM entries WHERE ID =%s", (entry_id,))
+ marked = cur.fetchall()[0][0]
+ if(marked == 0):
+ conn.execute(
+ "UPDATE entries SET Transferred = 1 WHERE ID =%s", (entry_id,))
+ else:
+ conn.execute(
+ "UPDATE entries SET Transferred = 0 WHERE ID =%s", (entry_id,))
return True
def check_entry_quota(client_id):
- conn = open_db()
- cur = conn.execute(
- "SELECT Count(*) FROM entries WHERE entries.Client_Id = %s", (client_id,))
+ with get_db_engine().connect() as conn:
+ cur = conn.execute(
+ "SELECT Count(*) FROM entries WHERE entries.Client_Id = %s", (client_id,))
return cur.fetchall()[0][0]
def check_queue_length():
- conn = open_db()
- cur = conn.execute("SELECT Count(*) FROM entries")
+ with get_db_engine().connect() as conn:
+ cur = conn.execute("SELECT Count(*) FROM entries")
return cur.fetchall()[0][0]
def clear_played_songs():
- conn = open_db()
- conn.execute("DELETE FROM done_songs")
- # conn.close()
+ with get_db_engine().connect() as conn:
+ conn.execute("DELETE FROM done_songs")
return True
def delete_entry(id):
- conn = open_db()
- conn.execute("DELETE FROM entries WHERE id=%s", (id,))
- # conn.close()
+ with get_db_engine().connect() as conn:
+ conn.execute("DELETE FROM entries WHERE id=%s", (id,))
return True
@@ -189,16 +169,15 @@ def delete_entries(ids):
for x in ids:
idlist.append((x,))
try:
- conn = open_db()
- cur = conn.execute("DELETE FROM entries WHERE id=%s", idlist)
- # conn.close()
+ with get_db_engine().connect() as conn:
+ cur = conn.execute("DELETE FROM entries WHERE id=%s", idlist)
+
return cur.rowcount
except Exception as error:
return -1
def delete_all_entries():
- conn = open_db()
- conn.execute("DELETE FROM entries")
- # conn.close()
+ with get_db_engine().connect() as conn:
+ conn.execute("DELETE FROM entries")
return True
diff --git a/docker-compose.prod.yml b/docker-compose.prod.yml
index 2ee48c0..a964cb2 100644
--- a/docker-compose.prod.yml
+++ b/docker-compose.prod.yml
@@ -1,10 +1,15 @@
version: "3.9"
+
+secrets:
+ secrets:
+ file: ./secrets.yml
+
services:
karaoqueue:
image: "phillipkhne/karaoqueue:latest"
restart: always
ports:
- - "127.0.0.1:8081:80"
+ - "127.0.0.1:8081:80" # Please put a reverse proxy in front of this
environment:
DEPLOYMENT_PLATFORM: Docker
DBSTRING: mysql://user:pass@host:3306/database
@@ -16,8 +21,8 @@ services:
image: mariadb
restart: always
environment:
- MARIADB_ROOT_PASSWORD: dpMAZj*Mc4%FZM!V
+ MARIADB_ROOT_PASSWORD: changeme!
MARIADB_ROOT_HOST: localhost
MARIADB_DATABASE: karaoqueue
MARIADB_USER: karaoqueue
- MARIADB_PASSWORD: a5G@P*^tCW$$w@wE
\ No newline at end of file
+ MARIADB_PASSWORD: change
\ No newline at end of file
diff --git a/docker-compose.yml b/docker-compose.yml
new file mode 100644
index 0000000..eafc0e2
--- /dev/null
+++ b/docker-compose.yml
@@ -0,0 +1,15 @@
+# This Compose file is for development only. It is not intended for production use.
+# It only starts auxiliary services, such as a database, that are required for the
+# application to run. The application itself is started separately, using the
+# command "python -m flask run" or your favorite IDE.
+# Useful for attaching a debugger to the application.
+
+version: "3.9"
+
+services:
+ db:
+ image: mariadb
+ restart: always
+ env_file: .env.dev
+ ports:
+ - "3306:3306"
\ No newline at end of file
From 551536bcb4d9fbb7d1fc1d7317d2018694bc5f4e Mon Sep 17 00:00:00 2001
From: =?UTF-8?q?Phillip=20K=C3=BChne?=
Date: Tue, 28 Mar 2023 01:00:21 +0200
Subject: [PATCH 26/85] Update bootstrap-tables
---
backend/templates/base.html | 6 +++---
1 file changed, 3 insertions(+), 3 deletions(-)
diff --git a/backend/templates/base.html b/backend/templates/base.html
index 8b598ea..70ebb30 100644
--- a/backend/templates/base.html
+++ b/backend/templates/base.html
@@ -12,7 +12,7 @@
{% block title %}{% endblock %} - KaraoQueue
-
+
-
+
+ src="https://unpkg.com/bootstrap-table@1.21.2/dist/extensions/auto-refresh/bootstrap-table-auto-refresh.min.js">
{% block extrajs %}{% endblock %}
From e84ff1a381bc4eb9c7d9ef93f7d6af8b3b17c58f Mon Sep 17 00:00:00 2001
From: =?UTF-8?q?Phillip=20K=C3=BChne?=
Date: Thu, 30 Mar 2023 17:05:17 +0200
Subject: [PATCH 39/85] Change container registry to github
---
docker-compose.prod.yml | 2 +-
1 file changed, 1 insertion(+), 1 deletion(-)
diff --git a/docker-compose.prod.yml b/docker-compose.prod.yml
index 3fe46c5..d8cf50d 100644
--- a/docker-compose.prod.yml
+++ b/docker-compose.prod.yml
@@ -6,7 +6,7 @@ secrets:
services:
karaoqueue:
- image: "phillipkhne/karaoqueue:latest"
+ image: "ghcr.io/phoenixtwofive/karaoqueue:v2023.03"
build: .
restart: always
ports:
From a2cf4fc47f878b0ad8c041069d084b1f8637c3d6 Mon Sep 17 00:00:00 2001
From: =?UTF-8?q?Phillip=20K=C3=BChne?=
Date: Thu, 30 Mar 2023 17:05:46 +0200
Subject: [PATCH 40/85] Change .env.dev to reflect new settings storage
---
.env.dev | 6 +-----
1 file changed, 1 insertion(+), 5 deletions(-)
diff --git a/.env.dev b/.env.dev
index 1fa9576..e22ecaa 100644
--- a/.env.dev
+++ b/.env.dev
@@ -7,8 +7,4 @@ MARIADB_PASSWORD=mariadb_karaoqueue_password
# Karaoqueue
DEPLOYMENT_PLATFORM=Docker
-DBSTRING=mysql://karaoqueue:mariadb_karaoqueue_password@127.0.0.1:3306/karaoqueue
-BASIC_AUTH_USERNAME=admin
-BASIC_AUTH_PASSWORD=change_me
-ENTRY_QUOTA=3
-MAX_QUEUE=20
\ No newline at end of file
+DBSTRING="mysql+pymysql://karaoqueue:mariadb_karaoqueue_password@127.0.0.1:3306/karaoqueue?charset=utf8mb4"
\ No newline at end of file
From 24458a78d0c45f13248d615df0fe11b9eaa76b9d Mon Sep 17 00:00:00 2001
From: =?UTF-8?q?Phillip=20K=C3=BChne?=
Date: Thu, 30 Mar 2023 17:06:36 +0200
Subject: [PATCH 41/85] Fix bugs
---
backend/app.py | 2 +-
backend/static/css/style.css | 2 +-
2 files changed, 2 insertions(+), 2 deletions(-)
diff --git a/backend/app.py b/backend/app.py
index fced038..0b20c7b 100644
--- a/backend/app.py
+++ b/backend/app.py
@@ -133,7 +133,7 @@ def update_songs():
return Response('{"status": "%s" }' % status, mimetype='text/json')
-@app.route("/api/songs/compl")
+@app.route("/api/songs/compl") # type: ignore
@nocache
def get_song_completions(input_string=""):
input_string = request.args.get('search', input_string)
diff --git a/backend/static/css/style.css b/backend/static/css/style.css
index 91b3f5c..46603a7 100644
--- a/backend/static/css/style.css
+++ b/backend/static/css/style.css
@@ -115,7 +115,7 @@ body {
}
.footer {
- background-color: #232323;
+ background-color: var(--background-color-var);
}
.modal-content {
From cf6d586856577ff7c8c4e3b6ddf252519e5b459f Mon Sep 17 00:00:00 2001
From: =?UTF-8?q?Phillip=20K=C3=BChne?=
Date: Thu, 30 Mar 2023 17:06:52 +0200
Subject: [PATCH 42/85] Overhaul database code
---
backend/database.py | 163 +++++++++++++++++++++++++++++---------------
backend/helpers.py | 12 +---
2 files changed, 109 insertions(+), 66 deletions(-)
diff --git a/backend/database.py b/backend/database.py
index d9bd68c..59129a2 100644
--- a/backend/database.py
+++ b/backend/database.py
@@ -2,7 +2,7 @@
from email.mime import base
from MySQLdb import Connection
-from sqlalchemy import create_engine, engine
+from sqlalchemy import create_engine, engine, text
import pandas
from io import StringIO
from flask import current_app
@@ -19,7 +19,8 @@ def get_db_engine() -> engine.base.Engine:
global sql_engine
if (not sql_engine):
print(current_app.config.get("DBCONNSTRING"))
- sql_engine = create_engine(current_app.config.get("DBCONNSTRING")) # type: ignore
+ sql_engine = create_engine(
+ current_app.config.get("DBCONNSTRING")) # type: ignore
return sql_engine
@@ -28,147 +29,169 @@ def import_songs(song_csv):
df = pandas.read_csv(StringIO(song_csv), sep=';')
with get_db_engine().connect() as conn:
df.to_sql(song_table, conn, if_exists='replace',
- index=False)
- cur = conn.execute("SELECT Count(Id) FROM songs")
- num_songs = cur.fetchone()[0] # type: ignore
+ index=False)
+ cur = conn.execute(text("SELECT Count(Id) FROM songs"))
+ num_songs = cur.fetchone()[0] # type: ignore
+ conn.commit()
print("Imported songs ({} in Database)".format(num_songs))
- return("Imported songs ({} in Database)".format(num_songs))
+ return ("Imported songs ({} in Database)".format(num_songs))
def create_entry_table():
with get_db_engine().connect() as conn:
- conn.execute('CREATE TABLE IF NOT EXISTS '+entry_table +
- ' (ID INTEGER PRIMARY KEY NOT NULL AUTO_INCREMENT, Song_Id INTEGER NOT NULL, Name VARCHAR(255), Client_Id VARCHAR(36), Transferred INTEGER DEFAULT 0)')
+ stmt = text(
+ f'CREATE TABLE IF NOT EXISTS `{entry_table}` (ID INTEGER PRIMARY KEY NOT NULL AUTO_INCREMENT, Song_Id INTEGER NOT NULL, Name VARCHAR(255), Client_Id VARCHAR(36), Transferred INTEGER DEFAULT 0)')
+ conn.execute(stmt)
+ conn.commit()
def create_done_song_table():
with get_db_engine().connect() as conn:
- conn.execute('CREATE TABLE IF NOT EXISTS '+done_table +
- ' (Song_Id INTEGER PRIMARY KEY NOT NULL, Plays INTEGER)')
+ stmt = text(
+ f'CREATE TABLE IF NOT EXISTS `{done_table}` (Song_Id INTEGER PRIMARY KEY NOT NULL, Plays INTEGER)')
+ conn.execute(stmt)
+ conn.commit()
def create_song_table():
with get_db_engine().connect() as conn:
- conn.execute("CREATE TABLE IF NOT EXISTS `"+song_table+"""` (
+ stmt = text(f"""CREATE TABLE IF NOT EXISTS `{song_table}` (
`Id` INTEGER,
`Title` TEXT,
`Artist` TEXT,
- `Year` INTEGER,
- `Duo` INTEGER,
+ `Year` VARCHAR(4),
+ `Duo` BOOLEAN,
`Explicit` INTEGER,
- `Date Added` TEXT,
+ `Date Added` TIMESTAMP,
`Styles` TEXT,
`Languages` TEXT
- )""")
+ )""")
+ conn.execute(stmt)
+ conn.commit()
def create_list_view():
with get_db_engine().connect() as conn:
- conn.execute("""CREATE OR REPLACE VIEW `Liste` AS
+ stmt = text("""CREATE OR REPLACE VIEW `Liste` AS
SELECT Name, Title, Artist, entries.Id AS entry_ID, songs.Id AS song_ID, entries.Transferred
FROM entries, songs
WHERE entries.Song_Id=songs.Id""")
+ conn.execute(stmt)
+ conn.commit()
def create_done_song_view():
with get_db_engine().connect() as conn:
- conn.execute("""CREATE OR REPLACE VIEW `Abspielliste` AS
+ stmt = text("""CREATE OR REPLACE VIEW `Abspielliste` AS
SELECT CONCAT(Artist," - ", Title) AS Song, Plays AS Wiedergaben
FROM songs, done_songs
WHERE done_songs.Song_Id=songs.Id""")
-
+ conn.execute(stmt)
+ conn.commit()
+
def create_config_table():
with get_db_engine().connect() as conn:
- conn.execute("""CREATE TABLE IF NOT EXISTS `config` (
+ stmt = text("""CREATE TABLE IF NOT EXISTS `config` (
`Key` VARCHAR(50) NOT NULL PRIMARY KEY,
`Value` TEXT
- )""")
+ )""")
+ conn.execute(stmt)
+ conn.commit()
def get_list():
with get_db_engine().connect() as conn:
- cur = conn.execute("SELECT * FROM Liste")
+ stmt = text("SELECT * FROM Liste")
+ cur = conn.execute(stmt)
return cur.fetchall()
def get_played_list():
with get_db_engine().connect() as conn:
- cur = conn.execute("SELECT * FROM Abspielliste")
+ stmt = text("SELECT * FROM Abspielliste")
+ cur = conn.execute(stmt)
return cur.fetchall()
def get_song_list():
with get_db_engine().connect() as conn:
- cur = conn.execute(
- "SELECT Artist || \" - \" || Title AS Song, Id FROM songs;")
+ stmt = text("SELECT Artist || \" - \" || Title AS Song, Id FROM songs;")
+ cur = conn.execute(stmt)
return cur.fetchall()
def get_song_completions(input_string):
with get_db_engine().connect() as conn:
- # Don't look, it burns...
- prepared_string = "%{0}%".format(
- input_string).upper() # "Test" -> "%TEST%"
- print(prepared_string)
+ prepared_string = f"%{input_string.upper()}%"
+ stmt = text(
+ "SELECT CONCAT(Artist, ' - ', Title) AS Song, Id FROM songs WHERE CONCAT(Artist, ' - ', Title) LIKE :prepared_string LIMIT 20;")
cur = conn.execute(
- "SELECT CONCAT(Artist,\" - \",Title) AS Song, Id FROM songs WHERE CONCAT(Artist,\" - \",Title) LIKE (%s) LIMIT 20;", [prepared_string])
- return cur.fetchall()
+ stmt, {"prepared_string": prepared_string}) # type: ignore
+ return cur.fetchall()
def add_entry(name, song_id, client_id):
with get_db_engine().connect() as conn:
- conn.execute(
- "INSERT INTO entries (Song_Id,Name,Client_Id) VALUES(%s,%s,%s);", (song_id, name, client_id))
- return
+ stmt = text(
+ "INSERT INTO entries (Song_Id,Name,Client_Id) VALUES (:par_song_id,:par_name,:par_client_id);")
+ conn.execute(stmt, {"par_song_id": song_id, "par_name": name,
+ "par_client_id": client_id}) # type: ignore
+ conn.commit()
+ return True
def add_sung_song(entry_id):
with get_db_engine().connect() as conn:
- cur = conn.execute(
- """SELECT Song_Id FROM entries WHERE Id=%s""", (entry_id,))
- song_id = cur.fetchone()[0] # type: ignore
- conn.execute("""INSERT INTO done_songs (Song_Id, Plays) VALUES("""+str(song_id)+""",1) ON DUPLICATE KEY UPDATE Plays=Plays + 1;""")
+ stmt = text("SELECT Song_Id FROM entries WHERE Id=:par_entry_id")
+ cur = conn.execute(stmt, {"par_entry_id": entry_id}) # type: ignore
+ song_id = cur.fetchone()[0] # type: ignore
+ stmt = text(
+ "INSERT INTO done_songs (Song_Id,Plays) VALUES (:par_song_id,1) ON DUPLICATE KEY UPDATE Plays=Plays + 1;")
+ conn.execute(stmt, {"par_song_id": song_id}) # type: ignore
delete_entry(entry_id)
+ conn.commit()
return True
def toggle_transferred(entry_id):
with get_db_engine().connect() as conn:
- cur = conn.execute(
- "SELECT Transferred FROM entries WHERE ID =%s", (entry_id,))
+ cur = conn.execute(text("SELECT Transferred FROM entries WHERE ID = :par_entry_id"),
+ {"par_entry_id": entry_id}) # type: ignore
marked = cur.fetchall()[0][0]
- if(marked == 0):
- conn.execute(
- "UPDATE entries SET Transferred = 1 WHERE ID =%s", (entry_id,))
+ if (marked == 0):
+ conn.execute(text("UPDATE entries SET Transferred = 1 WHERE ID = :par_entry_id"),
+ {"par_entry_id": entry_id}) # type: ignore
else:
- conn.execute(
- "UPDATE entries SET Transferred = 0 WHERE ID =%s", (entry_id,))
+ conn.execute(text("UPDATE entries SET Transferred = 0 WHERE ID = :par_entry_id"),
+ {"par_entry_id": entry_id}) # type: ignore
+ conn.commit()
return True
def check_entry_quota(client_id):
with get_db_engine().connect() as conn:
- cur = conn.execute(
- "SELECT Count(*) FROM entries WHERE entries.Client_Id = %s", (client_id,))
+ cur = conn.execute(text("SELECT Count(*) FROM entries WHERE entries.Client_Id = :par_client_id"),
+ {"par_client_id": client_id}) # type: ignore
return cur.fetchall()[0][0]
def check_queue_length():
with get_db_engine().connect() as conn:
- cur = conn.execute("SELECT Count(*) FROM entries")
+ cur = conn.execute(text("SELECT Count(*) FROM entries"))
return cur.fetchall()[0][0]
def clear_played_songs():
with get_db_engine().connect() as conn:
- conn.execute("DELETE FROM done_songs")
+ conn.execute(text("DELETE FROM done_songs"))
return True
def delete_entry(id):
with get_db_engine().connect() as conn:
- conn.execute("DELETE FROM entries WHERE id=%s", (id,))
+ conn.execute(text("DELETE FROM entries WHERE id= :par_id"), {
+ "par_id": id}) # type: ignore
return True
@@ -178,7 +201,8 @@ def delete_entries(ids):
idlist.append((x,))
try:
with get_db_engine().connect() as conn:
- cur = conn.execute("DELETE FROM entries WHERE id=%s", idlist)
+ cur = conn.execute(text("DELETE FROM entries WHERE id= :par_id"), {
+ "par_id": idlist})
return cur.rowcount
except Exception as error:
@@ -187,23 +211,50 @@ def delete_entries(ids):
def delete_all_entries() -> bool:
with get_db_engine().connect() as conn:
- conn.execute("DELETE FROM entries")
+ conn.execute(text("DELETE FROM entries"))
+ conn.commit()
return True
+
def get_config(key: str) -> str:
- with get_db_engine().connect() as conn:
- cur = conn.execute("SELECT `Value` FROM config WHERE `Key`=%s", (key,))
- return cur.fetchall()[0][0]
+ try:
+ with get_db_engine().connect() as conn:
+ cur = conn.execute(
+ text("SELECT `Value` FROM config WHERE `Key`= :par_key"), {"par_key": key}) # type: ignore
+ conn.commit()
+ return cur.fetchall()[0][0]
+ except IndexError as error:
+ return ""
+
def set_config(key: str, value: str) -> bool:
+ print(f"Setting config {key} to {value}")
with get_db_engine().connect() as conn:
- conn.execute("INSERT INTO config (`Key`, `Value`) VALUES (%s,%s) ON DUPLICATE KEY UPDATE `Value`=%s", (key, value, value))
+ conn.execute(text(
+ "INSERT INTO config (`Key`, `Value`) VALUES ( :par_key , :par_value) ON DUPLICATE KEY UPDATE `Value`= :par_value"),
+ {"par_key": key, "par_value": value}
+ ) # type: ignore
+ conn.commit()
return True
+
def get_config_list() -> dict:
with get_db_engine().connect() as conn:
- cur = conn.execute("SELECT * FROM config")
+ cur = conn.execute(text("SELECT * FROM config"))
result_dict = {}
for row in cur.fetchall():
result_dict[row[0]] = row[1]
return result_dict
+
+
+def check_config_table() -> bool:
+ with get_db_engine().connect() as conn:
+ if conn.dialect.has_table(conn, 'config'):
+ # type: ignore
+ # type: ignore
+ if (conn.execute(text("SELECT COUNT(*) FROM config")).fetchone()[0] > 0): # type: ignore
+ return True
+ else:
+ return False
+ else:
+ return False
diff --git a/backend/helpers.py b/backend/helpers.py
index 24cd637..03fb713 100644
--- a/backend/helpers.py
+++ b/backend/helpers.py
@@ -34,15 +34,7 @@ def is_valid_uuid(val):
return False
def check_config_exists():
- eng = database.get_db_engine()
- with eng.connect() as conn:
- if conn.dialect.has_table(conn, 'config'):
- if (conn.execute("SELECT COUNT(*) FROM config").fetchone()[0] > 0): # type: ignore
- return True
- else:
- return False
- else:
- return False
+ return database.check_config_table()
def load_version(app: Flask):
if os.environ.get("SOURCE_VERSION"):
@@ -87,7 +79,7 @@ def setup_config(app: Flask):
config = database.get_config_list()
print("Loaded existing config")
else:
- config = {'username': 'admin', 'password': 'changeme', 'entryquota': 3, 'maxqueue': 20, 'entries_allowed': 1, 'theme': 'default'}
+ config = {'username': 'admin', 'password': 'changeme', 'entryquota': 3, 'maxqueue': 20, 'entries_allowed': 1, 'theme': 'default.css'}
for key, value in config.items():
database.set_config(key, value)
print("Created new config")
From ca73d57567e75f969cbd156a378417309c6a0e15 Mon Sep 17 00:00:00 2001
From: =?UTF-8?q?Phillip=20K=C3=BChne?=
Date: Thu, 30 Mar 2023 17:07:01 +0200
Subject: [PATCH 43/85] Update Requirements
---
backend/requirements.txt | 31 +++++++++++++++++++++++++++----
1 file changed, 27 insertions(+), 4 deletions(-)
diff --git a/backend/requirements.txt b/backend/requirements.txt
index d816517..b00ae16 100644
--- a/backend/requirements.txt
+++ b/backend/requirements.txt
@@ -1,7 +1,30 @@
-requests
-pandas
-Flask-BasicAuth
+autopep8
+beautifulsoup4
bs4
+certifi
+charset-normalizer
+click
+Flask
+Flask-BasicAuth
+greenlet
gunicorn
+idna
+itsdangerous
+Jinja2
+mariadb
+MarkupSafe
+mysql
+mysqlclient
+numpy
+pandas
+pycodestyle
+PyMySQL
+python-dateutil
+pytz
+requests
+six
+soupsieve
SQLAlchemy
-mysqlclient
\ No newline at end of file
+toml
+urllib3
+Werkzeug
From 429ffddced5a0d6fe427cf06b384fc5863a67ed0 Mon Sep 17 00:00:00 2001
From: =?UTF-8?q?Phillip=20K=C3=BChne?=
Date: Thu, 30 Mar 2023 17:38:34 +0200
Subject: [PATCH 44/85] Update data_adapters for sqlalchemy upgrade
---
backend/data_adapters.py | 5 +----
1 file changed, 1 insertion(+), 4 deletions(-)
diff --git a/backend/data_adapters.py b/backend/data_adapters.py
index e3437bc..20cd2b4 100644
--- a/backend/data_adapters.py
+++ b/backend/data_adapters.py
@@ -1,8 +1,5 @@
-def dict_from_row(row):
- return dict(zip(row.keys(), row))
-
def dict_from_rows(rows):
outlist=[]
for row in rows:
- outlist.append(dict_from_row(row))
+ outlist.append(dict(row._mapping))
return outlist
\ No newline at end of file
From 16cb9e7d5a6a6c1af48814bb7736652e85474a63 Mon Sep 17 00:00:00 2001
From: =?UTF-8?q?Phillip=20K=C3=BChne?=
Date: Thu, 30 Mar 2023 17:39:13 +0200
Subject: [PATCH 45/85] Fix theme setting persistence
---
backend/helpers.py | 1 +
1 file changed, 1 insertion(+)
diff --git a/backend/helpers.py b/backend/helpers.py
index 03fb713..36afedd 100644
--- a/backend/helpers.py
+++ b/backend/helpers.py
@@ -88,6 +88,7 @@ def setup_config(app: Flask):
app.config['ENTRY_QUOTA'] = config['entryquota']
app.config['MAX_QUEUE'] = config['maxqueue']
app.config['ENTRIES_ALLOWED'] = bool(config['entries_allowed'])
+ app.config['THEME'] = config['theme']
# set queue admittance
def set_accept_entries(app: Flask, allowed: bool):
From 12207c1246f130815b8826fdb36f7dae24bb341d Mon Sep 17 00:00:00 2001
From: =?UTF-8?q?Phillip=20K=C3=BChne?=
Date: Thu, 30 Mar 2023 17:39:44 +0200
Subject: [PATCH 46/85] Admin credentials can be changed via settings
---
backend/app.py | 20 +++++++++++++++-----
backend/templates/settings.html | 12 ++++++++++++
2 files changed, 27 insertions(+), 5 deletions(-)
diff --git a/backend/app.py b/backend/app.py
index 0b20c7b..66817bf 100644
--- a/backend/app.py
+++ b/backend/app.py
@@ -83,6 +83,9 @@ def settings_post():
entryquota = request.form.get("entryquota")
maxqueue = request.form.get("maxqueue")
theme = request.form.get("theme")
+ username = request.form.get("username")
+ password = request.form.get("password")
+ changed_credentials = False
if entryquota.isnumeric() and int(entryquota) > 0: # type: ignore
app.config['ENTRY_QUOTA'] = int(entryquota) # type: ignore
else:
@@ -91,14 +94,21 @@ def settings_post():
app.config['MAX_QUEUE'] = int(maxqueue) # type: ignore
else:
abort(400)
- if theme in helpers.get_themes():
- app.config['THEME'] = theme
+ if theme is not None and theme in helpers.get_themes():
+ helpers.set_theme(app,theme)
else:
abort(400)
-
+ if username != "" and username != app.config['BASIC_AUTH_USERNAME']:
+ app.config['BASIC_AUTH_USERNAME'] = username
+ changed_credentials = True
+ if password != "":
+ app.config['BASIC_AUTH_PASSWORD'] = password
+ changed_credentials = True
helpers.persist_config(app=app)
-
- return render_template('settings.html', app=app, auth=basic_auth.authenticate())
+ if changed_credentials:
+ return redirect("/")
+ else:
+ return render_template('settings.html', app=app, auth=basic_auth.authenticate(), themes=helpers.get_themes())
@app.route("/api/queue")
diff --git a/backend/templates/settings.html b/backend/templates/settings.html
index 8ea5118..7722183 100644
--- a/backend/templates/settings.html
+++ b/backend/templates/settings.html
@@ -18,6 +18,18 @@
{% endfor %}
+
+
+ Warnung: Änderungen an den folgenden Einstellungen führen zu einer sofortigen Abmeldung!
+
+
+
+
+
+
+
+
+
From 7ef938a5ff5b3adf39174bed06b08d4b4873699c Mon Sep 17 00:00:00 2001
From: =?UTF-8?q?Phillip=20K=C3=BChne?=
Date: Thu, 30 Mar 2023 17:51:20 +0200
Subject: [PATCH 47/85] Create new config with credentials from env vars
---
.env.dev | 4 ++-
backend/helpers.py | 63 +++++++++++++++++++++++++++++++++++-----------
2 files changed, 51 insertions(+), 16 deletions(-)
diff --git a/.env.dev b/.env.dev
index e22ecaa..df90e9c 100644
--- a/.env.dev
+++ b/.env.dev
@@ -7,4 +7,6 @@ MARIADB_PASSWORD=mariadb_karaoqueue_password
# Karaoqueue
DEPLOYMENT_PLATFORM=Docker
-DBSTRING="mysql+pymysql://karaoqueue:mariadb_karaoqueue_password@127.0.0.1:3306/karaoqueue?charset=utf8mb4"
\ No newline at end of file
+DBSTRING="mysql+pymysql://karaoqueue:mariadb_karaoqueue_password@127.0.0.1:3306/karaoqueue?charset=utf8mb4"
+INITIAL_USERNAME=admin
+INITIAL_PASSWORD=changeme
\ No newline at end of file
diff --git a/backend/helpers.py b/backend/helpers.py
index 36afedd..0fc7e22 100644
--- a/backend/helpers.py
+++ b/backend/helpers.py
@@ -11,6 +11,7 @@ import database
data_directory = "data"
config_file = data_directory+"/config.json"
+
def create_data_directory():
if not os.path.exists(data_directory):
os.makedirs(data_directory)
@@ -19,13 +20,16 @@ def create_data_directory():
def get_catalog_url():
r = requests.get('https://www.karafun.de/karaoke-song-list.html')
soup = BeautifulSoup(r.content, 'html.parser')
- url = soup.findAll('a', href=True, text='Verfügbar in CSV-Format')[0]['href']
+ url = soup.findAll(
+ 'a', href=True, text='Verfügbar in CSV-Format')[0]['href']
return url
+
def get_songs(url):
r = requests.get(url)
return r.text
+
def is_valid_uuid(val):
try:
uuid.UUID(str(val))
@@ -33,12 +37,15 @@ def is_valid_uuid(val):
except ValueError:
return False
+
def check_config_exists():
return database.check_config_table()
+
def load_version(app: Flask):
if os.environ.get("SOURCE_VERSION"):
- app.config['VERSION'] = os.environ.get("SOURCE_VERSION")[0:7] # type: ignore
+ app.config['VERSION'] = os.environ.get("SOURCE_VERSION")[
+ 0:7] # type: ignore
elif os.path.isfile(".version"):
with open('.version', 'r') as file:
data = file.read().replace('\n', '')
@@ -48,21 +55,22 @@ def load_version(app: Flask):
app.config['VERSION'] = ""
else:
app.config['VERSION'] = ""
-
+
+
def load_dbconfig(app: Flask):
if os.environ.get("FLASK_ENV") == "development":
app.config['DBCONNSTRING'] = os.environ.get("DBSTRING")
else:
- if os.environ.get("DEPLOYMENT_PLATFORM") == "Heroku":
+ if os.environ.get("DEPLOYMENT_PLATFORM") == "Heroku":
if os.environ.get("JAWSDB_MARIA_URL"):
app.config['DBCONNSTRING'] = os.environ.get("JAWSDB_MARIA_URL")
else:
app.config['DBCONNSTRING'] = ""
- if os.environ.get("DEPLOYMENT_PLATFORM") == "Docker":
+ if os.environ.get("DEPLOYMENT_PLATFORM") == "Docker":
if os.environ.get("DBSTRING"):
app.config['DBCONNSTRING'] = os.environ.get("DBSTRING")
else:
- app.config['DBCONNSTRING'] = ""
+ app.config['DBCONNSTRING'] = ""
elif os.path.isfile(".dbconn"):
with open('.dbconn', 'r') as file:
data = file.read().replace('\n', '')
@@ -74,15 +82,31 @@ def load_dbconfig(app: Flask):
exit("No database connection string found. Cannot continue. Please set the environment variable DBSTRING or create a file .dbconn in the root directory of the project.")
# Check if config exists in DB, if not, create it.
+
+
def setup_config(app: Flask):
- if check_config_exists():
- config = database.get_config_list()
- print("Loaded existing config")
- else:
- config = {'username': 'admin', 'password': 'changeme', 'entryquota': 3, 'maxqueue': 20, 'entries_allowed': 1, 'theme': 'default.css'}
- for key, value in config.items():
+ if check_config_exists() == False:
+ print("No config found, creating new config"):
+ initial_username = os.environ.get("INITIAL_USERNAME")
+ initial_password = os.environ.get("INITIAL_PASSWORD")
+ if initial_username is None:
+ print(
+ "No initial username set. Please set the environment variable INITIAL_USERNAME")
+ exit()
+ if initial_password is None:
+ print(
+ "No initial password set. Please set the environment variable INITIAL_PASSWORD")
+ exit()
+ default_config = {'username': initial_username,
+ 'password': initial_password,
+ 'entryquota': 3,
+ 'maxqueue': 20,
+ 'entries_allowed': 1,
+ 'theme': 'default.css'}
+ for key, value in default_config.items():
database.set_config(key, value)
print("Created new config")
+ config = database.get_config_list()
app.config['BASIC_AUTH_USERNAME'] = config['username']
app.config['BASIC_AUTH_PASSWORD'] = config['password']
app.config['ENTRY_QUOTA'] = config['entryquota']
@@ -91,6 +115,8 @@ def setup_config(app: Flask):
app.config['THEME'] = config['theme']
# set queue admittance
+
+
def set_accept_entries(app: Flask, allowed: bool):
if allowed:
app.config['ENTRIES_ALLOWED'] = True
@@ -100,6 +126,8 @@ def set_accept_entries(app: Flask, allowed: bool):
database.set_config('entries_allowed', '0')
# get queue admittance
+
+
def get_accept_entries(app: Flask) -> bool:
state = bool(int(database.get_config('entries_allowed')))
app.config['ENTRIES_ALLOWED'] = state
@@ -108,11 +136,14 @@ def get_accept_entries(app: Flask) -> bool:
# Write settings from current app.config to DB
def persist_config(app: Flask):
- config = {'username': app.config['BASIC_AUTH_USERNAME'], 'password': app.config['BASIC_AUTH_PASSWORD'], 'entryquota': app.config['ENTRY_QUOTA'], 'maxqueue': app.config['MAX_QUEUE']}
+ config = {'username': app.config['BASIC_AUTH_USERNAME'], 'password': app.config['BASIC_AUTH_PASSWORD'],
+ 'entryquota': app.config['ENTRY_QUOTA'], 'maxqueue': app.config['MAX_QUEUE']}
for key, value in config.items():
database.set_config(key, value)
# Get available themes from themes directory
+
+
def get_themes():
themes = []
for theme in os.listdir('./static/css/themes'):
@@ -120,6 +151,8 @@ def get_themes():
return themes
# Set theme
+
+
def set_theme(app: Flask, theme: str):
if theme in get_themes():
app.config['THEME'] = theme
@@ -137,5 +170,5 @@ def nocache(view):
response.headers['Pragma'] = 'no-cache'
response.headers['Expires'] = '-1'
return response
-
- return update_wrapper(no_cache, view)
\ No newline at end of file
+
+ return update_wrapper(no_cache, view)
From fe71fa2d8cc78b634b37835fb193039253bbd36e Mon Sep 17 00:00:00 2001
From: =?UTF-8?q?Phillip=20K=C3=BChne?=
Date: Thu, 30 Mar 2023 18:00:08 +0200
Subject: [PATCH 48/85] Fix Caching
---
backend/app.py | 2 +-
1 file changed, 1 insertion(+), 1 deletion(-)
diff --git a/backend/app.py b/backend/app.py
index 66817bf..bc8727a 100644
--- a/backend/app.py
+++ b/backend/app.py
@@ -266,7 +266,7 @@ def add_header(response):
and also to cache the rendered page for 10 minutes.
"""
if not 'Cache-Control' in response.headers:
- response.headers['Cache-Control'] = 'private, max-age=600'
+ response.headers['Cache-Control'] = 'private, max-age=600, no-cache, must-revalidate'
return response
@app.context_processor
From dc53d8a8b18b3a9a86eb420f4b24009f9da04900 Mon Sep 17 00:00:00 2001
From: =?UTF-8?q?Phillip=20K=C3=BChne?=
Date: Thu, 30 Mar 2023 18:00:23 +0200
Subject: [PATCH 49/85] Fix Typos
---
backend/helpers.py | 5 ++---
1 file changed, 2 insertions(+), 3 deletions(-)
diff --git a/backend/helpers.py b/backend/helpers.py
index 0fc7e22..d67d5d2 100644
--- a/backend/helpers.py
+++ b/backend/helpers.py
@@ -44,8 +44,7 @@ def check_config_exists():
def load_version(app: Flask):
if os.environ.get("SOURCE_VERSION"):
- app.config['VERSION'] = os.environ.get("SOURCE_VERSION")[
- 0:7] # type: ignore
+ app.config['VERSION'] = os.environ.get("SOURCE_VERSION")[0:7] # type: ignore # noqa: E501
elif os.path.isfile(".version"):
with open('.version', 'r') as file:
data = file.read().replace('\n', '')
@@ -86,7 +85,7 @@ def load_dbconfig(app: Flask):
def setup_config(app: Flask):
if check_config_exists() == False:
- print("No config found, creating new config"):
+ print("No config found, creating new config")
initial_username = os.environ.get("INITIAL_USERNAME")
initial_password = os.environ.get("INITIAL_PASSWORD")
if initial_username is None:
From f055a59a38234beb74fb371b2e4ed786ec85cad3 Mon Sep 17 00:00:00 2001
From: =?UTF-8?q?Phillip=20K=C3=BChne?=
Date: Thu, 30 Mar 2023 18:14:40 +0200
Subject: [PATCH 50/85] Fix Dockerfile with in-Container update
---
Dockerfile | 7 +++++++
1 file changed, 7 insertions(+)
diff --git a/Dockerfile b/Dockerfile
index 47aa3bf..cc88883 100644
--- a/Dockerfile
+++ b/Dockerfile
@@ -1,5 +1,12 @@
FROM tiangolo/uwsgi-nginx-flask:python3.10
+RUN apt-key adv --recv-keys --keyserver keyserver.ubuntu.com 0xcbcb082a1bb943db
+RUN curl -LsS https://downloads.mariadb.com/MariaDB/mariadb_repo_setup | bash
+
+RUN apt-get update
+RUN apt-get upgrade -y
+RUN apt-get dist-upgrade
+
COPY ./backend /app
RUN pip install -r /app/requirements.txt
\ No newline at end of file
From 1bfe3b5d4be887ce6a873cc05d865da722e63520 Mon Sep 17 00:00:00 2001
From: =?UTF-8?q?Phillip=20K=C3=BChne?=
Date: Thu, 30 Mar 2023 19:14:24 +0200
Subject: [PATCH 51/85] Update Dockerfile
---
Dockerfile | 8 +++++---
1 file changed, 5 insertions(+), 3 deletions(-)
diff --git a/Dockerfile b/Dockerfile
index cc88883..d1425c5 100644
--- a/Dockerfile
+++ b/Dockerfile
@@ -1,4 +1,4 @@
-FROM tiangolo/uwsgi-nginx-flask:python3.10
+FROM tiangolo/meinheld-gunicorn-flask:python3.9
RUN apt-key adv --recv-keys --keyserver keyserver.ubuntu.com 0xcbcb082a1bb943db
RUN curl -LsS https://downloads.mariadb.com/MariaDB/mariadb_repo_setup | bash
@@ -7,6 +7,8 @@ RUN apt-get update
RUN apt-get upgrade -y
RUN apt-get dist-upgrade
-COPY ./backend /app
+COPY ./backend/requirements.txt /app/requirements.txt
-RUN pip install -r /app/requirements.txt
\ No newline at end of file
+RUN pip install --no-cache-dir --upgrade -r /app/requirements.txt
+
+COPY ./backend /app
\ No newline at end of file
From 3f09b79844391fa8ba9be7e8bab5b1bbc99f4cb1 Mon Sep 17 00:00:00 2001
From: =?UTF-8?q?Phillip=20K=C3=BChne?=
Date: Thu, 30 Mar 2023 19:30:13 +0200
Subject: [PATCH 52/85] Fix Dockerfile for "new" WSGI server
---
Dockerfile | 2 ++
1 file changed, 2 insertions(+)
diff --git a/Dockerfile b/Dockerfile
index d1425c5..2b0ceb6 100644
--- a/Dockerfile
+++ b/Dockerfile
@@ -11,4 +11,6 @@ COPY ./backend/requirements.txt /app/requirements.txt
RUN pip install --no-cache-dir --upgrade -r /app/requirements.txt
+RUN pip install --no-cache-dir -U meinheld
+
COPY ./backend /app
\ No newline at end of file
From fad21ba6c5a8e8ef6c61f82082ecc4a9dcd24a7e Mon Sep 17 00:00:00 2001
From: =?UTF-8?q?Phillip=20K=C3=BChne?=
Date: Thu, 30 Mar 2023 19:30:27 +0200
Subject: [PATCH 53/85] Fix DELETE statements
---
backend/database.py | 6 ++++--
1 file changed, 4 insertions(+), 2 deletions(-)
diff --git a/backend/database.py b/backend/database.py
index 59129a2..b0d153c 100644
--- a/backend/database.py
+++ b/backend/database.py
@@ -149,8 +149,8 @@ def add_sung_song(entry_id):
stmt = text(
"INSERT INTO done_songs (Song_Id,Plays) VALUES (:par_song_id,1) ON DUPLICATE KEY UPDATE Plays=Plays + 1;")
conn.execute(stmt, {"par_song_id": song_id}) # type: ignore
- delete_entry(entry_id)
conn.commit()
+ delete_entry(entry_id)
return True
@@ -185,6 +185,7 @@ def check_queue_length():
def clear_played_songs():
with get_db_engine().connect() as conn:
conn.execute(text("DELETE FROM done_songs"))
+ conn.commit()
return True
@@ -192,6 +193,7 @@ def delete_entry(id):
with get_db_engine().connect() as conn:
conn.execute(text("DELETE FROM entries WHERE id= :par_id"), {
"par_id": id}) # type: ignore
+ conn.commit()
return True
@@ -203,7 +205,7 @@ def delete_entries(ids):
with get_db_engine().connect() as conn:
cur = conn.execute(text("DELETE FROM entries WHERE id= :par_id"), {
"par_id": idlist})
-
+ conn.commit()
return cur.rowcount
except Exception as error:
return -1
From 4c64144f3df2c0da5100799f18489f9c2dfb2abc Mon Sep 17 00:00:00 2001
From: =?UTF-8?q?Phillip=20K=C3=BChne?=
Date: Thu, 30 Mar 2023 21:05:36 +0200
Subject: [PATCH 54/85] Fix sticky Popups
---
backend/templates/main_admin.html | 133 ++++++++++++++----------------
1 file changed, 62 insertions(+), 71 deletions(-)
diff --git a/backend/templates/main_admin.html b/backend/templates/main_admin.html
index 5379dc2..64b9252 100644
--- a/backend/templates/main_admin.html
+++ b/backend/templates/main_admin.html
@@ -1,15 +1,13 @@
-
-
{% extends 'base.html' %}
{% block title %}Warteliste-Admin{% endblock %}
{% block content %}
@@ -18,24 +16,13 @@ table td:nth-child(2) {
-
+
-
+
|
@@ -53,15 +40,18 @@ table td:nth-child(2) {
From 035394c36b66b9abafb298f86598360aae0457b2 Mon Sep 17 00:00:00 2001
From: =?UTF-8?q?Phillip=20K=C3=BChne?=
Date: Thu, 30 Mar 2023 22:37:39 +0200
Subject: [PATCH 59/85] Rephrase Information text
---
backend/templates/main.html | 2 +-
backend/templates/songlist.html | 2 +-
2 files changed, 2 insertions(+), 2 deletions(-)
diff --git a/backend/templates/main.html b/backend/templates/main.html
index 469ede0..f0851ac 100644
--- a/backend/templates/main.html
+++ b/backend/templates/main.html
@@ -30,7 +30,7 @@ $.getJSON("/api/entries/accept", (data) => {
$("#bfb").prop("aria-disabled",true);
$("#bfb").prop("tabindex","-1");
$("#bfb").wrap("");
- $(".tooltip-span").prop("title", "Eintragungen sind leider nicht mehr möglich.")
+ $(".tooltip-span").prop("title", "Eintragungen sind leider momentan nicht möglich.")
$('[data-toggle="tooltip"]').tooltip()
}
})
diff --git a/backend/templates/songlist.html b/backend/templates/songlist.html
index e961c64..b1fb95a 100644
--- a/backend/templates/songlist.html
+++ b/backend/templates/songlist.html
@@ -118,7 +118,7 @@
$(".enqueueButton").prop("disabled", true)
$(".enqueueButton").prop("style", "pointer-events: none;")
$(".enqueueButton").wrap("");
- $(".tooltip-span").prop("title", "Eintragungen sind leider nicht mehr möglich.")
+ $(".tooltip-span").prop("title", "Eintragungen sind leider momentan nicht möglich.")
$('[data-toggle="tooltip"]').tooltip()
} else {
$(".enqueueButton").prop("disabled", false)
From 8c735866a302eb8cd449f312b2807572c1a1c683 Mon Sep 17 00:00:00 2001
From: =?UTF-8?q?Phillip=20K=C3=BChne?=
Date: Thu, 30 Mar 2023 22:45:08 +0200
Subject: [PATCH 60/85] Remove remnants of file based config
---
backend/app.py | 1 -
backend/helpers.py | 9 ---------
2 files changed, 10 deletions(-)
diff --git a/backend/app.py b/backend/app.py
index bc8727a..29f55fd 100644
--- a/backend/app.py
+++ b/backend/app.py
@@ -248,7 +248,6 @@ def admin():
def activate_job():
helpers.load_dbconfig(app)
helpers.load_version(app)
- helpers.create_data_directory()
database.create_entry_table()
database.create_song_table()
database.create_done_song_table()
diff --git a/backend/helpers.py b/backend/helpers.py
index d67d5d2..2b8d4eb 100644
--- a/backend/helpers.py
+++ b/backend/helpers.py
@@ -8,15 +8,6 @@ from functools import wraps, update_wrapper
from datetime import datetime
import database
-data_directory = "data"
-config_file = data_directory+"/config.json"
-
-
-def create_data_directory():
- if not os.path.exists(data_directory):
- os.makedirs(data_directory)
-
-
def get_catalog_url():
r = requests.get('https://www.karafun.de/karaoke-song-list.html')
soup = BeautifulSoup(r.content, 'html.parser')
From 3921a9ea76afd2d77536c61d9b723108dc7ad1ac Mon Sep 17 00:00:00 2001
From: =?UTF-8?q?Phillip=20K=C3=BChne?=
Date: Thu, 30 Mar 2023 22:56:09 +0200
Subject: [PATCH 61/85] Update docker-Compose
---
docker-compose.prod.yml | 2 +-
1 file changed, 1 insertion(+), 1 deletion(-)
diff --git a/docker-compose.prod.yml b/docker-compose.prod.yml
index f36ced8..3b1851d 100644
--- a/docker-compose.prod.yml
+++ b/docker-compose.prod.yml
@@ -6,7 +6,7 @@ secrets:
services:
karaoqueue:
- image: "ghcr.io/phoenixtwofive/karaoqueue:v2023.03.1"
+ image: "ghcr.io/phoenixtwofive/karaoqueue:v2023.03.2"
build: .
restart: always
ports:
From f32f02dc443fafd36ab577488a8430da52998040 Mon Sep 17 00:00:00 2001
From: =?UTF-8?q?Phillip=20K=C3=BChne?=
Date: Fri, 31 Mar 2023 20:57:34 +0200
Subject: [PATCH 62/85] Fix Sorting in List view
---
backend/database.py | 4 +++-
1 file changed, 3 insertions(+), 1 deletion(-)
diff --git a/backend/database.py b/backend/database.py
index b0d153c..59a0d4c 100644
--- a/backend/database.py
+++ b/backend/database.py
@@ -75,7 +75,9 @@ def create_list_view():
stmt = text("""CREATE OR REPLACE VIEW `Liste` AS
SELECT Name, Title, Artist, entries.Id AS entry_ID, songs.Id AS song_ID, entries.Transferred
FROM entries, songs
- WHERE entries.Song_Id=songs.Id""")
+ WHERE entries.Song_Id=songs.Id
+ ORDER BY entries.Id ASC
+ """)
conn.execute(stmt)
conn.commit()
From 10717e753b5179404d938c6702a3369346c96092 Mon Sep 17 00:00:00 2001
From: =?UTF-8?q?Phillip=20K=C3=BChne?=
Date: Fri, 31 Mar 2023 20:58:47 +0200
Subject: [PATCH 63/85] Update Compose File
---
docker-compose.prod.yml | 2 +-
1 file changed, 1 insertion(+), 1 deletion(-)
diff --git a/docker-compose.prod.yml b/docker-compose.prod.yml
index 3b1851d..4301aa2 100644
--- a/docker-compose.prod.yml
+++ b/docker-compose.prod.yml
@@ -6,7 +6,7 @@ secrets:
services:
karaoqueue:
- image: "ghcr.io/phoenixtwofive/karaoqueue:v2023.03.2"
+ image: "ghcr.io/phoenixtwofive/karaoqueue:v2023.03.3"
build: .
restart: always
ports:
From 58dd0dd93ba24e320650319d3044e125aacef2ba Mon Sep 17 00:00:00 2001
From: =?UTF-8?q?Phillip=20K=C3=BChne?=
Date: Sun, 2 Apr 2023 17:21:56 +0200
Subject: [PATCH 64/85] Add devcontainer.json setup
---
.devcontainer/devcontainer.json | 32 ++++++++++++++++++++++++++++++++
1 file changed, 32 insertions(+)
create mode 100644 .devcontainer/devcontainer.json
diff --git a/.devcontainer/devcontainer.json b/.devcontainer/devcontainer.json
new file mode 100644
index 0000000..b69688f
--- /dev/null
+++ b/.devcontainer/devcontainer.json
@@ -0,0 +1,32 @@
+{
+ "image": "mcr.microsoft.com/devcontainers/universal:2",
+ "hostRequirements": {
+ "cpus": 4
+ },
+ "waitFor": "onCreateCommand",
+ "updateContentCommand": "pip install -r requirements.txt",
+ "postCreateCommand": "",
+ "postAttachCommand": {
+ "server": "flask --debug run"
+ },
+ "portsAttributes": {
+ "5000": {
+ "label": "Application",
+ "onAutoForward": "openPreview"
+ }
+ },
+ "customizations": {
+ "codespaces": {
+ "openFiles": [
+ "templates/index.html"
+ ]
+ },
+ "vscode": {
+ "extensions": [
+ "ms-python.python"
+ ]
+ }
+ },
+ "forwardPorts": [5000]
+ }
+
\ No newline at end of file
From d2caaac4bcb8f3906cf8b79d3bdeb41600046a41 Mon Sep 17 00:00:00 2001
From: =?UTF-8?q?Phillip=20K=C3=BChne?=
Date: Tue, 25 Apr 2023 16:46:43 +0200
Subject: [PATCH 65/85] Codecheck (#54)
* Add GitHub Action
* Add Linting
* Add .editorconfig
---
.editorconfig | 36 ++++++++++++++++++++++++++++++++++++
.flake8 | 3 +++
.github/workflows/lint.yaml | 23 +++++++++++++++++++++++
.vscode/settings.json | 13 ++++++++++++-
backend/app.py | 18 ++++++++++--------
backend/data_adapters.py | 4 ++--
backend/database.py | 8 +++-----
backend/helpers.py | 8 +++++---
backend/main.py | 2 +-
9 files changed, 95 insertions(+), 20 deletions(-)
create mode 100644 .editorconfig
create mode 100644 .flake8
create mode 100644 .github/workflows/lint.yaml
diff --git a/.editorconfig b/.editorconfig
new file mode 100644
index 0000000..eabd9a7
--- /dev/null
+++ b/.editorconfig
@@ -0,0 +1,36 @@
+# EditorConfig is awesome: https://EditorConfig.org
+
+# top-most EditorConfig file
+root = true
+
+[*]
+indent_style = space
+indent_size = 4
+end_of_line = lf
+charset = utf-8
+trim_trailing_whitespace = false
+insert_final_newline = false
+
+[*.md]
+trim_trailing_whitespace = true
+
+[*.py]
+indent_size = 4
+
+[*.js]
+indent_size = 2
+
+[*.html]
+indent_size = 2
+
+[*.css]
+indent_size = 2
+
+[*.scss]
+indent_size = 2
+
+[*.yaml]
+indent_size = 2
+
+[*.yml]
+indent_size = 2
\ No newline at end of file
diff --git a/.flake8 b/.flake8
new file mode 100644
index 0000000..1cfd4e0
--- /dev/null
+++ b/.flake8
@@ -0,0 +1,3 @@
+[flake8]
+ignore = E501
+max-line-length = 120
\ No newline at end of file
diff --git a/.github/workflows/lint.yaml b/.github/workflows/lint.yaml
new file mode 100644
index 0000000..e83fef6
--- /dev/null
+++ b/.github/workflows/lint.yaml
@@ -0,0 +1,23 @@
+name: Lint
+
+on: [push, pull_request]
+
+jobs:
+ flake8_py3:
+ runs-on: ubuntu-latest
+ steps:
+ - name: Setup Python
+ uses: actions/setup-python@v4.6.0
+ with:
+ python-version: '3.10'
+ architecture: x64
+ - name: Checkout PyTorch
+ uses: actions/checkout@master
+ - name: Install flake8
+ run: pip install flake8
+ - name: Run flake8
+ uses: suo/flake8-github-action@releases/v1
+ with:
+ checkName: 'flake8_py3' # NOTE: this needs to be the same as the job name
+ env:
+ GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}
\ No newline at end of file
diff --git a/.vscode/settings.json b/.vscode/settings.json
index d2a6c12..93f9daa 100644
--- a/.vscode/settings.json
+++ b/.vscode/settings.json
@@ -1,3 +1,14 @@
{
- "python.pythonPath": "/usr/bin/python"
+ "python.pythonPath": "/usr/bin/python",
+ "python.testing.unittestArgs": [
+ "-v",
+ "-s",
+ "./backend/tests",
+ "-p",
+ "*test.py"
+ ],
+ "python.testing.pytestEnabled": false,
+ "python.testing.unittestEnabled": true,
+ "python.linting.pylintEnabled": false,
+ "python.linting.flake8Enabled": true
}
\ No newline at end of file
diff --git a/backend/app.py b/backend/app.py
index 29f55fd..6ea7dab 100644
--- a/backend/app.py
+++ b/backend/app.py
@@ -1,5 +1,5 @@
from flask import Flask, render_template, abort, request, redirect, send_from_directory, jsonify
-from flask.wrappers import Request, Response
+from flask.wrappers import Response
import helpers
import database
import data_adapters
@@ -12,6 +12,7 @@ app = Flask(__name__, static_url_path='/static')
basic_auth = BasicAuth(app)
accept_entries = True
+
@app.route("/")
def home():
if basic_auth.authenticate():
@@ -95,7 +96,7 @@ def settings_post():
else:
abort(400)
if theme is not None and theme in helpers.get_themes():
- helpers.set_theme(app,theme)
+ helpers.set_theme(app, theme)
else:
abort(400)
if username != "" and username != app.config['BASIC_AUTH_USERNAME']:
@@ -143,7 +144,7 @@ def update_songs():
return Response('{"status": "%s" }' % status, mimetype='text/json')
-@app.route("/api/songs/compl") # type: ignore
+@app.route("/api/songs/compl") # type: ignore
@nocache
def get_song_completions(input_string=""):
input_string = request.args.get('search', input_string)
@@ -176,7 +177,7 @@ def delete_entries():
return
updates = database.delete_entries(request.json)
if updates >= 0:
- return Response('{"status": "OK", "updates": '+str(updates)+'}', mimetype='text/json')
+ return Response('{"status": "OK", "updates": ' + str(updates) + '}', mimetype='text/json')
else:
return Response('{"status": "FAIL"}', mimetype='text/json', status=400)
@@ -190,6 +191,7 @@ def mark_sung(entry_id):
else:
return Response('{"status": "FAIL"}', mimetype='text/json')
+
@app.route("/api/entries/mark_transferred/")
@nocache
@basic_auth.required
@@ -205,7 +207,7 @@ def mark_transferred(entry_id):
@basic_auth.required
def set_accept_entries(value):
if (value == '0' or value == '1'):
- helpers.set_accept_entries(app,bool(int(value)))
+ helpers.set_accept_entries(app, bool(int(value)))
return Response('{"status": "OK"}', mimetype='text/json')
else:
return Response('{"status": "FAIL"}', mimetype='text/json', status=400)
@@ -215,7 +217,7 @@ def set_accept_entries(value):
@nocache
def get_accept_entries():
accept_entries = helpers.get_accept_entries(app)
- return Response('{"status": "OK", "value": '+str(int(accept_entries))+'}', mimetype='text/json')
+ return Response('{"status": "OK", "value": ' + str(int(accept_entries)) + '}', mimetype='text/json')
@app.route("/api/played/clear")
@@ -257,17 +259,17 @@ def activate_job():
helpers.setup_config(app)
-
@app.after_request
def add_header(response):
"""
Add headers to both force latest IE rendering engine or Chrome Frame,
and also to cache the rendered page for 10 minutes.
"""
- if not 'Cache-Control' in response.headers:
+ if 'Cache-Control' not in response.headers:
response.headers['Cache-Control'] = 'private, max-age=600, no-cache, must-revalidate'
return response
+
@app.context_processor
def inject_version():
return dict(karaoqueue_version=app.config['VERSION'])
diff --git a/backend/data_adapters.py b/backend/data_adapters.py
index 20cd2b4..0136322 100644
--- a/backend/data_adapters.py
+++ b/backend/data_adapters.py
@@ -1,5 +1,5 @@
def dict_from_rows(rows):
- outlist=[]
+ outlist = []
for row in rows:
outlist.append(dict(row._mapping))
- return outlist
\ No newline at end of file
+ return outlist
diff --git a/backend/database.py b/backend/database.py
index 59a0d4c..fdf6d86 100644
--- a/backend/database.py
+++ b/backend/database.py
@@ -1,7 +1,5 @@
# -*- coding: utf_8 -*-
-from email.mime import base
-from MySQLdb import Connection
from sqlalchemy import create_engine, engine, text
import pandas
from io import StringIO
@@ -209,7 +207,7 @@ def delete_entries(ids):
"par_id": idlist})
conn.commit()
return cur.rowcount
- except Exception as error:
+ except Exception:
return -1
@@ -227,7 +225,7 @@ def get_config(key: str) -> str:
text("SELECT `Value` FROM config WHERE `Key`= :par_key"), {"par_key": key}) # type: ignore
conn.commit()
return cur.fetchall()[0][0]
- except IndexError as error:
+ except IndexError:
return ""
@@ -256,7 +254,7 @@ def check_config_table() -> bool:
if conn.dialect.has_table(conn, 'config'):
# type: ignore
# type: ignore
- if (conn.execute(text("SELECT COUNT(*) FROM config")).fetchone()[0] > 0): # type: ignore
+ if (conn.execute(text("SELECT COUNT(*) FROM config")).fetchone()[0] > 0): # type: ignore
return True
else:
return False
diff --git a/backend/helpers.py b/backend/helpers.py
index 2b8d4eb..fff6b8d 100644
--- a/backend/helpers.py
+++ b/backend/helpers.py
@@ -1,6 +1,5 @@
import requests
from bs4 import BeautifulSoup
-import json
import os
import uuid
from flask import make_response, Flask
@@ -8,6 +7,7 @@ from functools import wraps, update_wrapper
from datetime import datetime
import database
+
def get_catalog_url():
r = requests.get('https://www.karafun.de/karaoke-song-list.html')
soup = BeautifulSoup(r.content, 'html.parser')
@@ -69,13 +69,15 @@ def load_dbconfig(app: Flask):
else:
app.config['DBCONNSTRING'] = ""
else:
- exit("No database connection string found. Cannot continue. Please set the environment variable DBSTRING or create a file .dbconn in the root directory of the project.")
+ exit("""No database connection string found. Cannot continue.
+ Please set the environment variable DBSTRING or
+ create a file .dbconn in the root directory of the project.""")
# Check if config exists in DB, if not, create it.
def setup_config(app: Flask):
- if check_config_exists() == False:
+ if check_config_exists() is False:
print("No config found, creating new config")
initial_username = os.environ.get("INITIAL_USERNAME")
initial_password = os.environ.get("INITIAL_PASSWORD")
diff --git a/backend/main.py b/backend/main.py
index 210a38e..6026b0f 100644
--- a/backend/main.py
+++ b/backend/main.py
@@ -1,4 +1,4 @@
from app import app
if __name__ == "__main__":
- app.run()
\ No newline at end of file
+ app.run()
From adebf35d08a1091dedb9cf35ace77ae35ecba70c Mon Sep 17 00:00:00 2001
From: =?UTF-8?q?Phillip=20K=C3=BChne?=
Date: Tue, 25 Apr 2023 16:52:36 +0200
Subject: [PATCH 66/85] Update lint.yaml
Remove action run on every push
---
.github/workflows/lint.yaml | 4 ++--
1 file changed, 2 insertions(+), 2 deletions(-)
diff --git a/.github/workflows/lint.yaml b/.github/workflows/lint.yaml
index e83fef6..6812373 100644
--- a/.github/workflows/lint.yaml
+++ b/.github/workflows/lint.yaml
@@ -1,6 +1,6 @@
name: Lint
-on: [push, pull_request]
+on: [pull_request]
jobs:
flake8_py3:
@@ -20,4 +20,4 @@ jobs:
with:
checkName: 'flake8_py3' # NOTE: this needs to be the same as the job name
env:
- GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}
\ No newline at end of file
+ GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}
From 865df5d58866ff0c1f2f17e505943fe4df2de89f Mon Sep 17 00:00:00 2001
From: =?UTF-8?q?Phillip=20K=C3=BChne?=
Date: Wed, 26 Apr 2023 18:08:03 +0200
Subject: [PATCH 67/85] Implement EventID to scope ClientIDs and Entry IDs
Implement an EventID saved in settings. Currently this is used to scope
clientIDs and entryIDs to an event. The client checks the event currently going on on
the server, and discards its localstorage (containing the clientID) if
it has changed
---
backend/app.py | 32 +++++++++++++++++++++++++---
backend/database.py | 37 ++++++++++++++++++++++++++++++++-
backend/helpers.py | 11 ++++++++++
backend/templates/base.html | 19 ++++++++++++++++-
backend/templates/songlist.html | 3 ++-
5 files changed, 96 insertions(+), 6 deletions(-)
diff --git a/backend/app.py b/backend/app.py
index 6ea7dab..b3ebe97 100644
--- a/backend/app.py
+++ b/backend/app.py
@@ -138,6 +138,7 @@ def songs():
@basic_auth.required
def update_songs():
database.delete_all_entries()
+ helpers.reset_current_event_id(app)
status = database.import_songs(
helpers.get_songs(helpers.get_catalog_url()))
print(status)
@@ -149,7 +150,6 @@ def update_songs():
def get_song_completions(input_string=""):
input_string = request.args.get('search', input_string)
if input_string != "":
- print(input_string)
result = [list(x) for x in database.get_song_completions(input_string=input_string)]
return jsonify(result)
@@ -157,10 +157,29 @@ def get_song_completions(input_string=""):
return 400
-@app.route("/api/entries/delete/")
+@app.route("/api/entries/delete/", methods=['GET'])
@nocache
@basic_auth.required
-def delete_entry(entry_id):
+def delete_entry_admin(entry_id):
+ if database.delete_entry(entry_id):
+ return Response('{"status": "OK"}', mimetype='text/json')
+ else:
+ return Response('{"status": "FAIL"}', mimetype='text/json')
+
+
+@app.route("/api/entries/delete/", methods=['POST'])
+@nocache
+def delete_entry_user(entry_id):
+ if not request.json:
+ print(request.data)
+ abort(400)
+ client_id = request.json['client_id']
+ if not helpers.is_valid_uuid(client_id):
+ print(request.data)
+ abort(400)
+ if database.get_raw_entry(entry_id)['client_id'] != client_id: # type: ignore
+ print(request.data)
+ abort(403)
if database.delete_entry(entry_id):
return Response('{"status": "OK"}', mimetype='text/json')
else:
@@ -235,6 +254,7 @@ def clear_played_songs():
@basic_auth.required
def delete_all_entries():
if 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')
@@ -246,6 +266,12 @@ def admin():
return redirect("/", code=303)
+@app.route("/api/events/current")
+@nocache
+def get_current_event():
+ return Response('{"status": "OK", "event": "' + helpers.get_current_event_id(app) + '"}', mimetype='text/json')
+
+
@app.before_first_request
def activate_job():
helpers.load_dbconfig(app)
diff --git a/backend/database.py b/backend/database.py
index fdf6d86..68e264a 100644
--- a/backend/database.py
+++ b/backend/database.py
@@ -4,6 +4,7 @@ from sqlalchemy import create_engine, engine, text
import pandas
from io import StringIO
from flask import current_app
+import uuid
song_table = "songs"
entry_table = "entries"
@@ -16,7 +17,6 @@ sql_engine = None
def get_db_engine() -> engine.base.Engine:
global sql_engine
if (not sql_engine):
- print(current_app.config.get("DBCONNSTRING"))
sql_engine = create_engine(
current_app.config.get("DBCONNSTRING")) # type: ignore
return sql_engine
@@ -189,6 +189,26 @@ def clear_played_songs():
return True
+def get_entry(id):
+ try:
+ with get_db_engine().connect() as conn:
+ cur = conn.execute(text("SELECT * FROM Liste WHERE entry_ID = :par_id"),
+ {"par_id": id}) # type: ignore
+ return cur.fetchall()[0]
+ except Exception:
+ return None
+
+
+def get_raw_entry(id):
+ try:
+ with get_db_engine().connect() as conn:
+ cur = conn.execute(text("SELECT * FROM entries WHERE ID = :par_id"),
+ {"par_id": id}) # type: ignore
+ return cur.fetchall()[0]
+ except Exception:
+ return None
+
+
def delete_entry(id):
with get_db_engine().connect() as conn:
conn.execute(text("DELETE FROM entries WHERE id= :par_id"), {
@@ -260,3 +280,18 @@ def check_config_table() -> bool:
return False
else:
return False
+
+
+def init_event_id() -> bool:
+ if not get_config("EventID"):
+ set_config("EventID", str(uuid.uuid4()))
+ return True
+
+
+def reset_event_id() -> bool:
+ set_config("EventID", str(uuid.uuid4()))
+ return True
+
+
+def get_event_id() -> str:
+ return get_config("EventID")
diff --git a/backend/helpers.py b/backend/helpers.py
index fff6b8d..9dab0a5 100644
--- a/backend/helpers.py
+++ b/backend/helpers.py
@@ -98,6 +98,7 @@ def setup_config(app: Flask):
for key, value in default_config.items():
database.set_config(key, value)
print("Created new config")
+ database.init_event_id()
config = database.get_config_list()
app.config['BASIC_AUTH_USERNAME'] = config['username']
app.config['BASIC_AUTH_PASSWORD'] = config['password']
@@ -105,6 +106,7 @@ def setup_config(app: Flask):
app.config['MAX_QUEUE'] = config['maxqueue']
app.config['ENTRIES_ALLOWED'] = bool(config['entries_allowed'])
app.config['THEME'] = config['theme']
+ app.config['EVENT_ID'] = database.get_event_id()
# set queue admittance
@@ -153,6 +155,15 @@ def set_theme(app: Flask, theme: str):
print("Theme not found, not setting theme.")
+def get_current_event_id(app: Flask):
+ return app.config['EVENT_ID']
+
+
+def reset_current_event_id(app: Flask):
+ database.reset_event_id()
+ app.config['EVENT_ID'] = database.get_event_id()
+
+
def nocache(view):
@wraps(view)
def no_cache(*args, **kwargs):
diff --git a/backend/templates/base.html b/backend/templates/base.html
index 852d0eb..26d0ec2 100644
--- a/backend/templates/base.html
+++ b/backend/templates/base.html
@@ -108,7 +108,7 @@
{% block extrajs %}{% endblock %}