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, get_all_empfehlungen,
) )
from permissions import admin_required, login_required 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 datetime import datetime
from pathlib import Path from pathlib import Path
from copy import deepcopy from copy import deepcopy
app = Flask(__name__) app = Flask(__name__)
app.config.from_object(Config) app.config.from_object(Config)
@ -428,12 +429,16 @@ def admin_themen():
def admin_thema_new(): def admin_thema_new():
ansprechpartner = get_all_ansprechpartner() ansprechpartner = get_all_ansprechpartner()
branchen = get_all_branchen() branchen = get_all_branchen()
image_dir = Path(app.root_path) / "static" / "images"
image_files = get_image_files(image_dir)
if request.method == "POST": if request.method == "POST":
kurztitel = request.form.get("kurztitel", "").strip() kurztitel = request.form.get("kurztitel", "").strip()
titel = request.form.get("titel", "").strip() titel = request.form.get("titel", "").strip()
infotext = request.form.get("infotext", "").strip() infotext = request.form.get("infotext", "").strip()
zusatztext = request.form.get("zusatztext", "").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")] ansprechpartner_ids = [int(x) for x in request.form.getlist("ansprechpartner_ids")]
branche_ids = [int(x) for x in request.form.getlist("branche_ids")] branche_ids = [int(x) for x in request.form.getlist("branche_ids")]
@ -447,6 +452,7 @@ def admin_thema_new():
branchen=branchen, branchen=branchen,
selected_ansprechpartner_ids=ansprechpartner_ids, selected_ansprechpartner_ids=ansprechpartner_ids,
selected_branche_ids=branche_ids, selected_branche_ids=branche_ids,
image_files=image_files,
) )
create_thema( create_thema(
@ -454,6 +460,8 @@ def admin_thema_new():
titel, titel,
infotext, infotext,
zusatztext, zusatztext,
reihenfolge,
pic,
ansprechpartner_ids, ansprechpartner_ids,
branche_ids, branche_ids,
) )
@ -468,6 +476,7 @@ def admin_thema_new():
branchen=branchen, 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>/edit", methods=["GET", "POST"]) @app.route("/admin/themen/<int:thema_id>/edit", methods=["GET", "POST"])
@ -480,14 +489,19 @@ def admin_thema_edit(thema_id):
ansprechpartner = get_all_ansprechpartner() ansprechpartner = get_all_ansprechpartner()
branchen = get_all_branchen() branchen = get_all_branchen()
image_dir = Path(app.root_path) / "static" / "images"
image_files = get_image_files(image_dir)
if request.method == "POST": if request.method == "POST":
kurztitel = request.form.get("kurztitel", "").strip() kurztitel = request.form.get("kurztitel", "").strip()
titel = request.form.get("titel", "").strip() titel = request.form.get("titel", "").strip()
infotext = request.form.get("infotext", "").strip() infotext = request.form.get("infotext", "").strip()
zusatztext = request.form.get("zusatztext", "").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")] ansprechpartner_ids = [int(x) for x in request.form.getlist("ansprechpartner_ids")]
branche_ids = [int(x) for x in request.form.getlist("branche_ids")] branche_ids = [int(x) for x in request.form.getlist("branche_ids")]
if not kurztitel or not titel: if not kurztitel or not titel:
flash("Kurztitel und Titel sind Pflichtfelder.", "error") flash("Kurztitel und Titel sind Pflichtfelder.", "error")
@ -495,6 +509,8 @@ def admin_thema_edit(thema_id):
"id": thema_id, "id": thema_id,
"kurztitel": kurztitel, "kurztitel": kurztitel,
"titel": titel, "titel": titel,
"pic": pic,
"reihenfolge": reihenfolge,
"infotext": infotext, "infotext": infotext,
"zusatztext": zusatztext, "zusatztext": zusatztext,
} }
@ -506,6 +522,7 @@ def admin_thema_edit(thema_id):
branchen=branchen, branchen=branchen,
selected_ansprechpartner_ids=ansprechpartner_ids, selected_ansprechpartner_ids=ansprechpartner_ids,
selected_branche_ids=branche_ids, selected_branche_ids=branche_ids,
image_files=image_files,
) )
update_thema( update_thema(
@ -513,7 +530,9 @@ def admin_thema_edit(thema_id):
kurztitel, kurztitel,
titel, titel,
infotext, infotext,
reihenfolge,
zusatztext, zusatztext,
pic,
ansprechpartner_ids, ansprechpartner_ids,
branche_ids, branche_ids,
) )
@ -531,6 +550,7 @@ def admin_thema_edit(thema_id):
branchen=branchen, branchen=branchen,
selected_ansprechpartner_ids=selected_ansprechpartner_ids, selected_ansprechpartner_ids=selected_ansprechpartner_ids,
selected_branche_ids=selected_branche_ids, selected_branche_ids=selected_branche_ids,
image_files=image_files,
) )
@app.route("/admin/themen/<int:thema_id>/delete", methods=["POST"]) @app.route("/admin/themen/<int:thema_id>/delete", methods=["POST"])
@ -637,6 +657,7 @@ def admin_question_new():
if request.method == "POST": if request.method == "POST":
thema_id = request.form.get("thema_id") thema_id = request.form.get("thema_id")
text = request.form.get("text", "").strip() text = request.form.get("text", "").strip()
reihenfolge = request.form.get("reihenfolge", "").strip()
if not thema_id or not text: if not thema_id or not text:
flash("Thema und Text sind Pflichtfelder.", "error") flash("Thema und Text sind Pflichtfelder.", "error")
@ -647,7 +668,7 @@ def admin_question_new():
themen=themen, themen=themen,
) )
create_question(thema_id, text) create_question(thema_id, text, reihenfolge)
flash("Frage wurde erstellt.", "success") flash("Frage wurde erstellt.", "success")
return redirect(url_for("admin_questions")) return redirect(url_for("admin_questions"))
@ -670,6 +691,7 @@ def admin_question_edit(frage_id):
if request.method == "POST": if request.method == "POST":
thema_id = request.form.get("thema_id") thema_id = request.form.get("thema_id")
reihenfolge = request.form.get("reihenfolge", "").strip()
text = request.form.get("text", "").strip() text = request.form.get("text", "").strip()
if not thema_id or not text: if not thema_id or not text:
@ -677,6 +699,7 @@ def admin_question_edit(frage_id):
"id": frage_id, "id": frage_id,
"thema_id": thema_id, "thema_id": thema_id,
"text": text, "text": text,
"reihenfolge": reihenfolge,
} }
flash("Thema und Text sind Pflichtfelder.", "error") flash("Thema und Text sind Pflichtfelder.", "error")
return render_template( return render_template(
@ -686,7 +709,7 @@ def admin_question_edit(frage_id):
themen=themen, themen=themen,
) )
update_question(frage_id, thema_id, text) update_question(frage_id, thema_id, text, reihenfolge)
flash("Frage wurde gespeichert.", "success") flash("Frage wurde gespeichert.", "success")
return redirect(url_for("admin_questions")) return redirect(url_for("admin_questions"))
@ -915,6 +938,11 @@ def admin_empfehlungen():
ansprechpartner=ansprechpartner, 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) @app.errorhandler(401)
def unauthorized_error(error): def unauthorized_error(error):
return render_template("401.html"), 401 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(): def get_all_themen():
return fetch_all( return fetch_all(
""" """
SELECT id, kurztitel, titel, infotext, zusatztext SELECT id, kurztitel, titel, infotext, zusatztext, reihenfolge, pic
FROM thema FROM thema
ORDER BY id ORDER BY reihenfolge,id
""" """
) )
@ -175,7 +175,7 @@ def get_all_themen():
def get_thema_by_id(thema_id): def get_thema_by_id(thema_id):
return fetch_one( return fetch_one(
""" """
SELECT id, kurztitel, titel, infotext, zusatztext SELECT id, kurztitel, titel, infotext, zusatztext, reihenfolge, pic
FROM thema FROM thema
WHERE id = %s WHERE id = %s
""", """,
@ -242,17 +242,17 @@ def get_ansprechpartner_ids_for_thema(thema_id):
return [row["ansprechpartner_id"] for row in rows] 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: if branche_ids is None:
branche_ids = [] branche_ids = []
row = execute_returning( row = execute_returning(
""" """
INSERT INTO thema (kurztitel, titel, infotext, zusatztext) INSERT INTO thema (kurztitel, titel, infotext, zusatztext, reihenfolge, pic)
VALUES (%s, %s, %s, %s) VALUES (%s, %s, %s, %s, %s, %s)
RETURNING id RETURNING id
""", """,
(kurztitel, titel, infotext, zusatztext), (kurztitel, titel, infotext, zusatztext, reihenfolge, pic),
) )
thema_id = row["id"] thema_id = row["id"]
@ -277,7 +277,7 @@ def create_thema(kurztitel, titel, infotext, zusatztext, ansprechpartner_ids, br
return thema_id 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: if branche_ids is None:
branche_ids = [] branche_ids = []
@ -287,10 +287,12 @@ def update_thema(thema_id, kurztitel, titel, infotext, zusatztext, ansprechpartn
SET kurztitel = %s, SET kurztitel = %s,
titel = %s, titel = %s,
infotext = %s, infotext = %s,
zusatztext = %s zusatztext = %s,
reihenfolge = %s,
pic = %s
WHERE id = %s WHERE id = %s
""", """,
(kurztitel, titel, infotext, zusatztext, thema_id), (kurztitel, titel, infotext, zusatztext, reihenfolge, pic, thema_id),
) )
execute( execute(
@ -343,7 +345,7 @@ def get_thema_questions(thema_id):
SELECT id, thema_id, text SELECT id, thema_id, text
FROM fragen FROM fragen
WHERE thema_id = %s WHERE thema_id = %s
ORDER BY id ORDER BY reihenfolge, id
""", """,
(thema_id,), (thema_id,),
) )
@ -358,6 +360,7 @@ def get_all_questions_with_thema():
f.thema_id, f.thema_id,
t.id AS thema_sort_id, t.id AS thema_sort_id,
t.kurztitel, t.kurztitel,
t.reihenfolge,
t.titel, t.titel,
COALESCE(STRING_AGG(b.branchenname, ' | '), '') AS branchen COALESCE(STRING_AGG(b.branchenname, ' | '), '') AS branchen
FROM fragen f FROM fragen f
@ -374,7 +377,7 @@ def get_all_questions_with_thema():
t.id, t.id,
t.kurztitel, t.kurztitel,
t.titel 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): def get_question_by_id(frage_id):
return fetch_one( return fetch_one(
""" """
SELECT id, thema_id, text SELECT id, thema_id, text, reihenfolge
FROM fragen FROM fragen
WHERE id = %s 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( return execute_returning(
""" """
INSERT INTO fragen (thema_id, text) INSERT INTO fragen (thema_id, text, reihenfolge)
VALUES (%s, %s) VALUES (%s, %s, %s)
RETURNING id 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( execute(
""" """
UPDATE fragen UPDATE fragen
SET thema_id = %s, SET thema_id = %s,
text = %s text = %s,
reihenfolge = %s
WHERE id = %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): def get_thema_for_branche(thema_id, branche_id):
return fetch_one( 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 FROM thema t
JOIN branchenthemen bt ON bt.thema_id = t.id JOIN branchenthemen bt ON bt.thema_id = t.id
WHERE t.id = %s WHERE t.id = %s
@ -754,11 +763,13 @@ def get_all_themen_with_question_count():
t.titel, t.titel,
t.infotext, t.infotext,
t.zusatztext, t.zusatztext,
t.reihenfolge,
t.pic,
COUNT(f.id) AS fragen_anzahl COUNT(f.id) AS fragen_anzahl
FROM thema t FROM thema t
LEFT JOIN fragen f ON f.thema_id = t.id LEFT JOIN fragen f ON f.thema_id = t.id
GROUP BY t.id, t.kurztitel, t.titel, t.infotext, t.zusatztext 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> </select>
</div> </div>
<div class="form-group">
<label>Reihenfolge</label>
<input type="number"
name="reihenfolge"
value="{{ frage.reihenfolge or 0 }}">
</div>
<div class="form-group"> <div class="form-group">
<label>Frage</label> <label>Frage</label>
<textarea name="text" rows="5" required>{{ frage.text or '' }}</textarea> <textarea name="text" rows="5" required>{{ frage.text or '' }}</textarea>

View File

@ -11,6 +11,13 @@
</h1> </h1>
<form method="post" class="admin-form"> <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"> <div class="form-group">
<label for="kurztitel">Kurztitel</label> <label for="kurztitel">Kurztitel</label>
<input type="text" id="kurztitel" name="kurztitel" value="{{ thema.kurztitel or '' }}" required> <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> <input type="text" id="titel" name="titel" value="{{ thema.titel or '' }}" required>
</div> </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"> <div class="form-group">
<label for="infotext">InfoText</label> <label for="infotext">InfoText</label>
<textarea id="infotext" name="infotext" rows="6">{{ thema.infotext or '' }}</textarea> <textarea id="infotext" name="infotext" rows="6">{{ thema.infotext or '' }}</textarea>

View File

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

View File

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

View File

@ -7,6 +7,7 @@ import matplotlib
matplotlib.use('Agg') matplotlib.use('Agg')
import matplotlib.pyplot as plt import matplotlib.pyplot as plt
from config import Config from config import Config
from flask import current_app
from pathlib import Path from pathlib import Path
from weasyprint import HTML from weasyprint import HTML
@ -116,3 +117,29 @@ def create_chart_bytes(labels, yes_counts):
plt.close(fig) plt.close(fig)
buffer.seek(0) buffer.seek(0)
return buffer 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