Compare commits
7 Commits
| Author | SHA1 | Date | |
|---|---|---|---|
| 512ba98500 | |||
| fc1734d326 | |||
| 269decb110 | |||
| 5074455dac | |||
| d4cf9a53dd | |||
| b1891fb51d | |||
|
|
5ba472db42 |
14
app/crud.py
14
app/crud.py
@@ -2,6 +2,7 @@ import app.models as models
|
|||||||
from sqlalchemy.orm.attributes import flag_modified
|
from sqlalchemy.orm.attributes import flag_modified
|
||||||
|
|
||||||
from sqlalchemy.sql import text
|
from sqlalchemy.sql import text
|
||||||
|
import pickle
|
||||||
|
|
||||||
def get_songs_and_vote_for_session(db, session_name) -> list[models.Song]:
|
def get_songs_and_vote_for_session(db, session_name) -> list[models.Song]:
|
||||||
session_entry = activate_session(db, session_name)
|
session_entry = activate_session(db, session_name)
|
||||||
@@ -21,6 +22,7 @@ def get_all_songs_and_votes(db) -> list:
|
|||||||
songs.aca_artist,
|
songs.aca_artist,
|
||||||
songs.title,
|
songs.title,
|
||||||
songs.url,
|
songs.url,
|
||||||
|
songs.categories,
|
||||||
COUNT(vote) FILTER (where vote = -1) as "nein" ,
|
COUNT(vote) FILTER (where vote = -1) as "nein" ,
|
||||||
COUNT(vote) FILTER (where vote = 0) as "neutral",
|
COUNT(vote) FILTER (where vote = 0) as "neutral",
|
||||||
COUNT(vote) FILTER (where vote = 1) as "ja"
|
COUNT(vote) FILTER (where vote = 1) as "ja"
|
||||||
@@ -28,7 +30,17 @@ def get_all_songs_and_votes(db) -> list:
|
|||||||
GROUP BY song_id ORDER BY song_id
|
GROUP BY song_id ORDER BY song_id
|
||||||
""")).fetchall()
|
""")).fetchall()
|
||||||
|
|
||||||
return [dict(r._mapping) for r in res]
|
def process_row(r):
|
||||||
|
e = dict(r._mapping)
|
||||||
|
c = pickle.loads(e["categories"])
|
||||||
|
c = {k : bool(v) for k, v in c.items()}
|
||||||
|
e["categories"] = c
|
||||||
|
|
||||||
|
return e
|
||||||
|
|
||||||
|
res = [process_row(r) for r in res]
|
||||||
|
|
||||||
|
return res
|
||||||
|
|
||||||
|
|
||||||
def create_song(db,
|
def create_song(db,
|
||||||
|
|||||||
@@ -3,12 +3,14 @@ import numpy as np
|
|||||||
import re
|
import re
|
||||||
import requests
|
import requests
|
||||||
import os
|
import os
|
||||||
from fastapi import APIRouter, Security, Depends
|
from fastapi import APIRouter, Security, Depends, HTTPException, Response
|
||||||
|
from fastapi.responses import JSONResponse
|
||||||
from sqlalchemy.orm import Session
|
from sqlalchemy.orm import Session
|
||||||
|
|
||||||
from app.database import get_db, engine, Base
|
from app.database import get_db, engine, Base
|
||||||
from app.security import get_current_user
|
from app.security import get_current_user
|
||||||
from app.crud import create_song, get_setting, set_setting, get_all_songs_and_votes
|
from app.crud import create_song, get_setting, set_setting, get_all_songs_and_votes
|
||||||
|
from typing import Any
|
||||||
|
|
||||||
router = APIRouter(
|
router = APIRouter(
|
||||||
prefix="/admin",
|
prefix="/admin",
|
||||||
@@ -130,5 +132,13 @@ async def toggle_veto_mode(db: Session = Depends(get_db)) -> bool:
|
|||||||
|
|
||||||
|
|
||||||
@router.get("/results")
|
@router.get("/results")
|
||||||
async def get_evaluation(db = Depends(get_db)) -> list:
|
async def get_evaluation(format: str = "json", db = Depends(get_db)) -> Any:
|
||||||
return get_all_songs_and_votes(db)
|
res = get_all_songs_and_votes(db)
|
||||||
|
if format == "json":
|
||||||
|
return JSONResponse(content=res)
|
||||||
|
elif format == "csv":
|
||||||
|
df = pd.json_normalize(res)
|
||||||
|
df.columns = [c.split(".")[-1] for c in df.columns]
|
||||||
|
return Response(content=df.to_csv(), media_type="application/csv", headers={'Content-Disposition': 'attachment; filename="results.csv"'})
|
||||||
|
else:
|
||||||
|
raise HTTPException(status_code=404, detail="format must be json or csv")
|
||||||
|
|||||||
127
static/site.css
127
static/site.css
@@ -52,6 +52,24 @@
|
|||||||
font-family: Fira Sans, Helvetica, Arial, sans-serif;
|
font-family: Fira Sans, Helvetica, Arial, sans-serif;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
html {
|
||||||
|
background: var(--color-background);
|
||||||
|
}
|
||||||
|
|
||||||
|
header {
|
||||||
|
background-color: var(--color-choriosity-red);
|
||||||
|
height: 80px;
|
||||||
|
width: 100%;
|
||||||
|
display: flex;
|
||||||
|
align-items: center;
|
||||||
|
margin-bottom: 40px;
|
||||||
|
}
|
||||||
|
|
||||||
|
::selection {
|
||||||
|
background: var(--color-choriosity-red);
|
||||||
|
color: var(--color-white-100);
|
||||||
|
}
|
||||||
|
|
||||||
.vote-buttons,
|
.vote-buttons,
|
||||||
.cover-container,
|
.cover-container,
|
||||||
.categories {
|
.categories {
|
||||||
@@ -73,7 +91,6 @@
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
.text {
|
.text {
|
||||||
padding: 0.3em;
|
padding: 0.3em;
|
||||||
margin-bottom: 0.7em;
|
margin-bottom: 0.7em;
|
||||||
@@ -87,39 +104,46 @@
|
|||||||
|
|
||||||
.song {
|
.song {
|
||||||
background-color: var(--color-choriosity-red--light);
|
background-color: var(--color-choriosity-red--light);
|
||||||
|
background-color: var(--color-white-100);
|
||||||
|
border: 1px solid var(--color-choriosity-red);
|
||||||
|
border: 1px solid hsl(calc(var(--hue) * 360), 100%, 40%);
|
||||||
|
box-shadow: 4px 4px 8px var(--color-white-90);
|
||||||
padding: 0.4em;
|
padding: 0.4em;
|
||||||
border-radius: 0.5em;
|
border-radius: 0.3em;
|
||||||
width: 30em;
|
width: 30em;
|
||||||
margin-bottom: 1rem;
|
margin-bottom: 1rem;
|
||||||
margin-left: 0.5em;
|
margin-left: 0.5em;
|
||||||
float: left;
|
float: left;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
.cover-container {
|
.cover-container {
|
||||||
position: relative;
|
position: relative;
|
||||||
width: 10.67em;
|
width: 11.91em;
|
||||||
height: 6em;
|
height: 6.7em;
|
||||||
float: left;
|
float: left;
|
||||||
margin-right: 1em;
|
margin-right: 1em;
|
||||||
text-align: center;
|
/*background-color: white;*/
|
||||||
background-color: white;
|
display: flex;
|
||||||
|
/*align-items: center;*/
|
||||||
|
justify-content: center;
|
||||||
}
|
}
|
||||||
|
|
||||||
.cover {
|
.cover {
|
||||||
display: block;
|
/*display: block;*/
|
||||||
max-width: 100%;
|
object-fit: contain;
|
||||||
|
/*max-width: 100%;
|
||||||
max-height: 100%;
|
max-height: 100%;
|
||||||
|
scale: 2;
|
||||||
position: absolute;
|
position: absolute;
|
||||||
top: 50%;
|
top: 75%;
|
||||||
left: 50%;
|
left: 75%;
|
||||||
-webkit-transform: translate(-50%, -50%);
|
-webkit-transform: translate(-50%, -50%);
|
||||||
-ms-transform: translate(-50%, -50%);
|
-ms-transform: translate(-50%, -50%);
|
||||||
transform: translate(-50%, -50%);
|
transform: translate(-50%, -50%);*/
|
||||||
transition: .3s ease;
|
transition: .3s ease;
|
||||||
|
border-radius: 0.3em;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
.overlay {
|
.overlay {
|
||||||
position: absolute;
|
position: absolute;
|
||||||
top: 0;
|
top: 0;
|
||||||
@@ -161,9 +185,9 @@
|
|||||||
height: 60%;
|
height: 60%;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
.vote-buttons {
|
.vote-buttons {
|
||||||
display: inline-block;
|
display: inline-block;
|
||||||
|
margin-top: 0.7em;
|
||||||
}
|
}
|
||||||
|
|
||||||
.button {
|
.button {
|
||||||
@@ -211,11 +235,11 @@
|
|||||||
}
|
}
|
||||||
|
|
||||||
.button:not(.selected):not(:hover) {
|
.button:not(.selected):not(:hover) {
|
||||||
background-color: #b0b0b0;
|
background-color: var(--color-white-90);
|
||||||
}
|
}
|
||||||
|
|
||||||
.button:hover {
|
.button:hover {
|
||||||
filter: drop-shadow(2px 2px 2px) brightness(95%);
|
filter: drop-shadow(2px 2px 2px var(--color-white-70)) brightness(95%);
|
||||||
cursor: pointer;
|
cursor: pointer;
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -236,7 +260,6 @@
|
|||||||
}
|
}
|
||||||
|
|
||||||
.categories {
|
.categories {
|
||||||
width: 60%;
|
|
||||||
overflow: hidden;
|
overflow: hidden;
|
||||||
/*cursor: grab;*/
|
/*cursor: grab;*/
|
||||||
white-space: nowrap;
|
white-space: nowrap;
|
||||||
@@ -249,6 +272,7 @@
|
|||||||
border-radius: 1.2em;
|
border-radius: 1.2em;
|
||||||
padding: 0 0.5em 0 0.5em;
|
padding: 0 0.5em 0 0.5em;
|
||||||
margin-right: 0.4em;
|
margin-right: 0.4em;
|
||||||
|
margin-top: 0.5em;
|
||||||
display: inline-block;
|
display: inline-block;
|
||||||
color: white;
|
color: white;
|
||||||
max-width: 10em;
|
max-width: 10em;
|
||||||
@@ -257,9 +281,12 @@
|
|||||||
/*color-mix(in srgb, var(--main-color) 60%, transparent);*/
|
/*color-mix(in srgb, var(--main-color) 60%, transparent);*/
|
||||||
}
|
}
|
||||||
|
|
||||||
.vote-buttons {
|
/*.vote-buttons {
|
||||||
margin-top: 0.5em;
|
margin-top: 1em;
|
||||||
}
|
width: 100%;
|
||||||
|
display: inline-flex;
|
||||||
|
justify-content: center;
|
||||||
|
}*/
|
||||||
|
|
||||||
.song-title,
|
.song-title,
|
||||||
.song-artist {
|
.song-artist {
|
||||||
@@ -273,28 +300,44 @@
|
|||||||
}
|
}
|
||||||
|
|
||||||
h1 {
|
h1 {
|
||||||
padding: 0.1em;
|
font-size: 2rem;
|
||||||
padding-left: 0.2em;
|
|
||||||
margin-bottom: 1rem;
|
|
||||||
font-size: 1.25rem;
|
|
||||||
background-color: var(--color-choriosity-red);
|
|
||||||
color: white;
|
color: white;
|
||||||
/*border-bottom: 0.3rem solid var(--color-choriosity-red--light);*/
|
|
||||||
clear: both;
|
clear: both;
|
||||||
}
|
}
|
||||||
|
|
||||||
#songs h1 .color {
|
h1#title {
|
||||||
border: 0.2rem solid color-mix(in srgb, hsl(calc(var(--hue) * 360), 100%, 40%) 50%, white);
|
margin-top: 30px;
|
||||||
background-color: hsl(calc(var(--hue) * 360), 100%, 40%);
|
|
||||||
border-radius: 0.5em;
|
|
||||||
margin-right: 1em;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
#songs h1 {
|
h2 {
|
||||||
border-left: 0.3em solid white;
|
padding: 0.1em;
|
||||||
box-shadow: -1em 0px 0px 0px hsl(calc(var(--hue) * 360), 100%, 40%);
|
padding-left: 0.2em;
|
||||||
margin-left: 1em;
|
margin-bottom: 1rem;
|
||||||
padding-left: 0.3em;
|
padding-top: 1.5rem;
|
||||||
|
font-size: 1.25rem;
|
||||||
|
clear: both;
|
||||||
|
display: flex;
|
||||||
|
justify-content: center;
|
||||||
|
align-items: center;
|
||||||
|
text-align: center;
|
||||||
|
}
|
||||||
|
|
||||||
|
h2:after {
|
||||||
|
content: '';
|
||||||
|
border-top: 2px solid hsl(calc(var(--hue) * 360), 100%, 40%);
|
||||||
|
flex: 1 0 20px;
|
||||||
|
margin: 0 0 0 10px;
|
||||||
|
}
|
||||||
|
|
||||||
|
h2:before {
|
||||||
|
content: '';
|
||||||
|
display: inline-block;
|
||||||
|
width: 15px;
|
||||||
|
height: 15px;
|
||||||
|
border-radius: 7.5px;
|
||||||
|
background-color: hsl(calc(var(--hue) * 360), 100%, 40%);
|
||||||
|
margin-right: 0.3em;
|
||||||
|
margin-left: 0.3em;
|
||||||
}
|
}
|
||||||
|
|
||||||
#yt-player,
|
#yt-player,
|
||||||
@@ -335,17 +378,9 @@ h1 {
|
|||||||
width: 1.5em;
|
width: 1.5em;
|
||||||
}
|
}
|
||||||
|
|
||||||
#title {
|
|
||||||
margin-top: 1em;
|
|
||||||
font-size: 2rem;
|
|
||||||
/*line-height: 2rem;*/
|
|
||||||
}
|
|
||||||
|
|
||||||
#title-logo {
|
#title-logo {
|
||||||
margin-top: -0.7em;
|
|
||||||
margin-bottom: -0.8em;
|
|
||||||
vertical-align: middle;
|
vertical-align: middle;
|
||||||
height: 3em;
|
height: 6rem;
|
||||||
filter: drop-shadow(0 0 1px var(--color-choriosity-red--medium));
|
filter: drop-shadow(0 0 1px var(--color-choriosity-red--medium));
|
||||||
margin-right: 1em;
|
margin: 30px 1em 0 1em;
|
||||||
}
|
}
|
||||||
@@ -168,17 +168,20 @@
|
|||||||
abgeben.
|
abgeben.
|
||||||
</div>
|
</div>
|
||||||
{% else %}
|
{% else %}
|
||||||
<h1 id="title"><img id="title-logo" src="https://choriosity.de/assets/images/logo-choriosity_weiss.svg"> Liederwahl
|
<header>
|
||||||
</h1>
|
<img id="title-logo" src="https://choriosity.de/assets/images/logo-choriosity_weiss.svg">
|
||||||
|
<h1 id="title">Liederwahl</h1>
|
||||||
|
</header>
|
||||||
|
|
||||||
<div class="text">Du kannst die Liederwahl jederzeit unterbrechen und zu einem späteren Zeitpunkt weitermachen. Mit
|
<div class="text">Du kannst die Liederwahl jederzeit unterbrechen und zu einem späteren Zeitpunkt weitermachen. Mit
|
||||||
einem Klick auf das Thumbnail kannst du das Lied abspielen.
|
einem Klick auf das Thumbnail kannst du das Lied abspielen.
|
||||||
</div>
|
</div>
|
||||||
{% endif %}
|
{% endif %}
|
||||||
<div id="songs">
|
<div id="songs">
|
||||||
{% for main_category, songs in songs_by_category.items() %}
|
{% for main_category, songs in songs_by_category.items() %}
|
||||||
<h1 style="--hue: {{ all_categories[main_category] / all_categories|length }};">{{ main_category }}</h1>
|
<h2 style="--hue: {{ all_categories[main_category] / all_categories|length }};">{{ main_category }}</h2>
|
||||||
{% for song in songs -%}
|
{% for song in songs -%}
|
||||||
<div class="song{% if (song.vote == -1) or (not song.vote and not song.singable) %} not_singable{% endif %}"
|
<div style="--hue: {{ all_categories[main_category] / all_categories|length }};" class="song{% if (song.vote == -1) or (not song.vote and not song.singable) %} not_singable{% endif %}"
|
||||||
id="song-{{ song.id }}">
|
id="song-{{ song.id }}">
|
||||||
<div class="cover-container">
|
<div class="cover-container">
|
||||||
<img src="{{ song.thumbnail }}" class="cover">
|
<img src="{{ song.thumbnail }}" class="cover">
|
||||||
@@ -194,7 +197,7 @@
|
|||||||
<div class="categories" id="container">{% for category_name, is_in_category in song.categories.items() %}{%
|
<div class="categories" id="container">{% for category_name, is_in_category in song.categories.items() %}{%
|
||||||
if is_in_category %}<span style="--hue: {{ all_categories[category_name] / all_categories|length }};">{{
|
if is_in_category %}<span style="--hue: {{ all_categories[category_name] / all_categories|length }};">{{
|
||||||
category_name }}</span>{%
|
category_name }}</span>{%
|
||||||
endif %}{% endfor %}<span style="--main-color: transparent;"> </span>
|
endif %}{% endfor %}<span style="--main-color: transparent; --hue: transparent;"> </span>
|
||||||
</div>
|
</div>
|
||||||
<div class="vote-buttons">
|
<div class="vote-buttons">
|
||||||
<div class="button button-no {% if (song.vote == -1) or (not song.vote and not song.singable) %}selected{% endif %}"
|
<div class="button button-no {% if (song.vote == -1) or (not song.vote and not song.singable) %}selected{% endif %}"
|
||||||
|
|||||||
Reference in New Issue
Block a user