7 Commits
1.2.0 ... main

Author SHA1 Message Date
512ba98500 simplify/fix cover position/size
All checks were successful
release-tag / release-image (push) Successful in 5m4s
2024-07-12 12:14:27 +02:00
fc1734d326 border radius styling
All checks were successful
release-tag / release-image (push) Successful in 4m33s
2024-07-12 11:39:51 +02:00
269decb110 fix minor styling issue
All checks were successful
release-tag / release-image (push) Successful in 4m46s
2024-07-12 11:25:37 +02:00
5074455dac styling
All checks were successful
release-tag / release-image (push) Successful in 5m1s
2024-07-12 11:13:10 +02:00
d4cf9a53dd export results to csv
All checks were successful
release-tag / release-image (push) Successful in 5m49s
2024-07-11 22:00:22 +02:00
b1891fb51d Change styling (Felix) 2024-07-11 19:42:07 +02:00
Matthias Weber
5ba472db42 add categories to evaluation
All checks were successful
release-tag / release-image (push) Successful in 4m49s
2024-07-10 21:52:48 +02:00
4 changed files with 115 additions and 55 deletions

View File

@@ -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,

View File

@@ -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")

View File

@@ -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;
} }

View File

@@ -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;">&nbsp;</span> endif %}{% endfor %}<span style="--main-color: transparent; --hue: transparent;">&nbsp;</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 %}"