reihenfolge und images

This commit is contained in:
Bernhard Kolb 2026-05-10 22:29:36 +02:00
parent 87b44885b7
commit c4a8b41f26
22 changed files with 130 additions and 27 deletions

34
app.py
View File

@ -58,11 +58,12 @@ from db import (
get_all_empfehlungen,
)
from permissions import admin_required, login_required
from tools import create_assessment_chart, generate_activation_token, send_mail, verify_activation_token, generate_pdf_from_html
from tools import create_assessment_chart, generate_activation_token, send_mail, verify_activation_token, generate_pdf_from_html, get_image_files
from datetime import datetime
from pathlib import Path
from copy import deepcopy
app = Flask(__name__)
app.config.from_object(Config)
@ -428,12 +429,16 @@ def admin_themen():
def admin_thema_new():
ansprechpartner = get_all_ansprechpartner()
branchen = get_all_branchen()
image_dir = Path(app.root_path) / "static" / "images"
image_files = get_image_files(image_dir)
if request.method == "POST":
kurztitel = request.form.get("kurztitel", "").strip()
titel = request.form.get("titel", "").strip()
infotext = request.form.get("infotext", "").strip()
zusatztext = request.form.get("zusatztext", "").strip()
pic = request.form.get("pic", "").strip()
reihenfolge = request.form.get("reihenfolge", "").strip()
ansprechpartner_ids = [int(x) for x in request.form.getlist("ansprechpartner_ids")]
branche_ids = [int(x) for x in request.form.getlist("branche_ids")]
@ -447,6 +452,7 @@ def admin_thema_new():
branchen=branchen,
selected_ansprechpartner_ids=ansprechpartner_ids,
selected_branche_ids=branche_ids,
image_files=image_files,
)
create_thema(
@ -454,6 +460,8 @@ def admin_thema_new():
titel,
infotext,
zusatztext,
reihenfolge,
pic,
ansprechpartner_ids,
branche_ids,
)
@ -468,6 +476,7 @@ def admin_thema_new():
branchen=branchen,
selected_ansprechpartner_ids=[],
selected_branche_ids=[],
image_files=image_files,
)
@app.route("/admin/themen/<int:thema_id>/edit", methods=["GET", "POST"])
@ -480,21 +489,28 @@ def admin_thema_edit(thema_id):
ansprechpartner = get_all_ansprechpartner()
branchen = get_all_branchen()
image_dir = Path(app.root_path) / "static" / "images"
image_files = get_image_files(image_dir)
if request.method == "POST":
kurztitel = request.form.get("kurztitel", "").strip()
titel = request.form.get("titel", "").strip()
infotext = request.form.get("infotext", "").strip()
zusatztext = request.form.get("zusatztext", "").strip()
pic = request.form.get("pic", "").strip()
reihenfolge = request.form.get("reihenfolge", "").strip()
ansprechpartner_ids = [int(x) for x in request.form.getlist("ansprechpartner_ids")]
branche_ids = [int(x) for x in request.form.getlist("branche_ids")]
if not kurztitel or not titel:
flash("Kurztitel und Titel sind Pflichtfelder.", "error")
thema_form = {
"id": thema_id,
"kurztitel": kurztitel,
"titel": titel,
"pic": pic,
"reihenfolge": reihenfolge,
"infotext": infotext,
"zusatztext": zusatztext,
}
@ -506,6 +522,7 @@ def admin_thema_edit(thema_id):
branchen=branchen,
selected_ansprechpartner_ids=ansprechpartner_ids,
selected_branche_ids=branche_ids,
image_files=image_files,
)
update_thema(
@ -513,7 +530,9 @@ def admin_thema_edit(thema_id):
kurztitel,
titel,
infotext,
reihenfolge,
zusatztext,
pic,
ansprechpartner_ids,
branche_ids,
)
@ -531,6 +550,7 @@ def admin_thema_edit(thema_id):
branchen=branchen,
selected_ansprechpartner_ids=selected_ansprechpartner_ids,
selected_branche_ids=selected_branche_ids,
image_files=image_files,
)
@app.route("/admin/themen/<int:thema_id>/delete", methods=["POST"])
@ -637,6 +657,7 @@ def admin_question_new():
if request.method == "POST":
thema_id = request.form.get("thema_id")
text = request.form.get("text", "").strip()
reihenfolge = request.form.get("reihenfolge", "").strip()
if not thema_id or not text:
flash("Thema und Text sind Pflichtfelder.", "error")
@ -647,7 +668,7 @@ def admin_question_new():
themen=themen,
)
create_question(thema_id, text)
create_question(thema_id, text, reihenfolge)
flash("Frage wurde erstellt.", "success")
return redirect(url_for("admin_questions"))
@ -670,6 +691,7 @@ def admin_question_edit(frage_id):
if request.method == "POST":
thema_id = request.form.get("thema_id")
reihenfolge = request.form.get("reihenfolge", "").strip()
text = request.form.get("text", "").strip()
if not thema_id or not text:
@ -677,6 +699,7 @@ def admin_question_edit(frage_id):
"id": frage_id,
"thema_id": thema_id,
"text": text,
"reihenfolge": reihenfolge,
}
flash("Thema und Text sind Pflichtfelder.", "error")
return render_template(
@ -686,7 +709,7 @@ def admin_question_edit(frage_id):
themen=themen,
)
update_question(frage_id, thema_id, text)
update_question(frage_id, thema_id, text, reihenfolge)
flash("Frage wurde gespeichert.", "success")
return redirect(url_for("admin_questions"))
@ -915,6 +938,11 @@ def admin_empfehlungen():
ansprechpartner=ansprechpartner,
)
@app.route("/images/<path:filename>")
def images(filename):
return send_from_directory(Path(app.root_path) / "/app/static/images", filename)
@app.errorhandler(401)
def unauthorized_error(error):
return render_template("401.html"), 401

55
db.py
View File

@ -165,9 +165,9 @@ def get_user_groups(user_id):
def get_all_themen():
return fetch_all(
"""
SELECT id, kurztitel, titel, infotext, zusatztext
SELECT id, kurztitel, titel, infotext, zusatztext, reihenfolge, pic
FROM thema
ORDER BY id
ORDER BY reihenfolge,id
"""
)
@ -175,7 +175,7 @@ def get_all_themen():
def get_thema_by_id(thema_id):
return fetch_one(
"""
SELECT id, kurztitel, titel, infotext, zusatztext
SELECT id, kurztitel, titel, infotext, zusatztext, reihenfolge, pic
FROM thema
WHERE id = %s
""",
@ -242,17 +242,17 @@ def get_ansprechpartner_ids_for_thema(thema_id):
return [row["ansprechpartner_id"] for row in rows]
def create_thema(kurztitel, titel, infotext, zusatztext, ansprechpartner_ids, branche_ids=None):
def create_thema(kurztitel, titel, infotext, zusatztext, reihenfolge, pic, ansprechpartner_ids, branche_ids=None):
if branche_ids is None:
branche_ids = []
row = execute_returning(
"""
INSERT INTO thema (kurztitel, titel, infotext, zusatztext)
VALUES (%s, %s, %s, %s)
INSERT INTO thema (kurztitel, titel, infotext, zusatztext, reihenfolge, pic)
VALUES (%s, %s, %s, %s, %s, %s)
RETURNING id
""",
(kurztitel, titel, infotext, zusatztext),
(kurztitel, titel, infotext, zusatztext, reihenfolge, pic),
)
thema_id = row["id"]
@ -277,7 +277,7 @@ def create_thema(kurztitel, titel, infotext, zusatztext, ansprechpartner_ids, br
return thema_id
def update_thema(thema_id, kurztitel, titel, infotext, zusatztext, ansprechpartner_ids, branche_ids=None):
def update_thema(thema_id, kurztitel, titel, infotext, reihenfolge, zusatztext, pic, ansprechpartner_ids, branche_ids=None):
if branche_ids is None:
branche_ids = []
@ -287,10 +287,12 @@ def update_thema(thema_id, kurztitel, titel, infotext, zusatztext, ansprechpartn
SET kurztitel = %s,
titel = %s,
infotext = %s,
zusatztext = %s
zusatztext = %s,
reihenfolge = %s,
pic = %s
WHERE id = %s
""",
(kurztitel, titel, infotext, zusatztext, thema_id),
(kurztitel, titel, infotext, zusatztext, reihenfolge, pic, thema_id),
)
execute(
@ -343,7 +345,7 @@ def get_thema_questions(thema_id):
SELECT id, thema_id, text
FROM fragen
WHERE thema_id = %s
ORDER BY id
ORDER BY reihenfolge, id
""",
(thema_id,),
)
@ -358,6 +360,7 @@ def get_all_questions_with_thema():
f.thema_id,
t.id AS thema_sort_id,
t.kurztitel,
t.reihenfolge,
t.titel,
COALESCE(STRING_AGG(b.branchenname, ' | '), '') AS branchen
FROM fragen f
@ -374,7 +377,7 @@ def get_all_questions_with_thema():
t.id,
t.kurztitel,
t.titel
ORDER BY t.id, f.id
ORDER BY t.reihenfolge, t.id, f.reihenfolge, f.id
"""
)
@ -489,7 +492,7 @@ def delete_ansprechpartner(ansprechpartner_id):
def get_question_by_id(frage_id):
return fetch_one(
"""
SELECT id, thema_id, text
SELECT id, thema_id, text, reihenfolge
FROM fragen
WHERE id = %s
""",
@ -497,26 +500,32 @@ def get_question_by_id(frage_id):
)
def create_question(thema_id, text):
def create_question(thema_id, text, reihenfolge):
return execute_returning(
"""
INSERT INTO fragen (thema_id, text)
VALUES (%s, %s)
INSERT INTO fragen (thema_id, text, reihenfolge)
VALUES (%s, %s, %s)
RETURNING id
""",
(thema_id, text),
(thema_id, text, reihenfolge),
)
def update_question(frage_id, thema_id, text):
def update_question(question_id, thema_id, text, reihenfolge):
execute(
"""
UPDATE fragen
SET thema_id = %s,
text = %s
text = %s,
reihenfolge = %s
WHERE id = %s
""",
(thema_id, text, frage_id),
(
thema_id,
text,
reihenfolge,
question_id,
),
)
@ -724,7 +733,7 @@ def get_next_thema_id_for_branche(current_thema_id, branche_id):
def get_thema_for_branche(thema_id, branche_id):
return fetch_one(
"""
SELECT t.id, t.kurztitel, t.titel, t.infotext, t.zusatztext
SELECT t.id, t.kurztitel, t.titel, t.infotext, t.zusatztext, t.pic
FROM thema t
JOIN branchenthemen bt ON bt.thema_id = t.id
WHERE t.id = %s
@ -754,11 +763,13 @@ def get_all_themen_with_question_count():
t.titel,
t.infotext,
t.zusatztext,
t.reihenfolge,
t.pic,
COUNT(f.id) AS fragen_anzahl
FROM thema t
LEFT JOIN fragen f ON f.thema_id = t.id
GROUP BY t.id, t.kurztitel, t.titel, t.infotext, t.zusatztext
ORDER BY t.id
ORDER BY t.reihenfolge, t.id
"""
)

Binary file not shown.

After

Width:  |  Height:  |  Size: 601 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 573 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 567 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 2.0 MiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 592 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 586 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 617 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 1.5 MiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 586 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 550 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 551 KiB

BIN
static/images/Logo-Raum.png Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 590 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 588 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 574 KiB

BIN
static/images/Logo-WLB.png Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 579 KiB

View File

@ -25,6 +25,13 @@
</select>
</div>
<div class="form-group">
<label>Reihenfolge</label>
<input type="number"
name="reihenfolge"
value="{{ frage.reihenfolge or 0 }}">
</div>
<div class="form-group">
<label>Frage</label>
<textarea name="text" rows="5" required>{{ frage.text or '' }}</textarea>

View File

@ -11,6 +11,13 @@
</h1>
<form method="post" class="admin-form">
<div class="form-group">
<label>Reihenfolge</label>
<input type="number"
name="reihenfolge"
value="{{ thema.reihenfolge or 0 }}">
</div>
<div class="form-group">
<label for="kurztitel">Kurztitel</label>
<input type="text" id="kurztitel" name="kurztitel" value="{{ thema.kurztitel or '' }}" required>
@ -21,6 +28,24 @@
<input type="text" id="titel" name="titel" value="{{ thema.titel or '' }}" required>
</div>
<!--div class="form-group">
<label for="kurztitel">Bild</label>
<input type="text" id="pic" name="pic" value="{{ thema.pic or '' }}" required>
</div-->
<div class="form-group">
<label for="pic">Bild / Logo</label>
<select id="pic" name="pic">
<option value="">-- kein Bild --</option>
{% for image in image_files %}
<option value="{{ image }}"
{% if thema.pic == image %}selected{% endif %}>
{{ image }}
</option>
{% endfor %}
</select>
</div>
<div class="form-group">
<label for="infotext">InfoText</label>
<textarea id="infotext" name="infotext" rows="6">{{ thema.infotext or '' }}</textarea>

View File

@ -11,9 +11,10 @@
<table class="admin-table">
<thead>
<tr>
<th>ID</th>
<th>Rfg</th>
<th>Kurztitel</th>
<th>Titel</th>
<th>Bild</th>
<th>Fragen</th>
<th>Aktionen</th>
</tr>
@ -21,9 +22,10 @@
<tbody>
{% for thema in themen %}
<tr>
<td>{{ thema.id }}</td>
<td>{{ thema.reihenfolge }}</td>
<td>{{ thema.kurztitel }}</td>
<td>{{ thema.titel }}</td>
<td>{{ thema.pic }}</td>
<td>
{{ thema.fragen_anzahl }}
{% if thema.fragen_anzahl < 8 %}

View File

@ -3,6 +3,9 @@
{% block content %}
<section class="card">
<h1>{{ thema.titel }}</h1>
{% if thema.pic != "" %}
<center><img src="/images/{{ thema.pic }}"></center>
{% endif %}
<p>{{ thema.infotext }}</p>
<p class="muted">{{ thema.zusatztext }}</p>
</section>

View File

@ -7,6 +7,7 @@ import matplotlib
matplotlib.use('Agg')
import matplotlib.pyplot as plt
from config import Config
from flask import current_app
from pathlib import Path
from weasyprint import HTML
@ -116,3 +117,29 @@ def create_chart_bytes(labels, yes_counts):
plt.close(fig)
buffer.seek(0)
return buffer
def get_image_files(image_dir):
allowed_extensions = {".png", ".jpg", ".jpeg", ".gif", ".webp", ".svg"}
#current_app.logger.error(f"IMAGE DIR: {image_dir}")
#current_app.logger.error(f"EXISTS: {image_dir.exists()}")
if not image_dir.exists():
return []
files = sorted(
file.name
for file in image_dir.iterdir()
if file.is_file()
and file.suffix.lower() in allowed_extensions
)
#current_app.logger.error(f"FILES: {files}")
return files