diff --git a/app/flask-postgres/app/app.py b/app/flask-postgres/app/app.py index dc4074b..a34057e 100644 --- a/app/flask-postgres/app/app.py +++ b/app/flask-postgres/app/app.py @@ -2126,4 +2126,103 @@ def pwdchange(): form_error=form_error, success_message=success_message, **get_current_user() + ) + +@app.route("/reporting/assessments") +@user_admin_required +def reporting_assessments(): + current_mandant_id = session.get("mandant_id") + mandant_level = session.get("mandant_level") + + conn = get_connection() + cur = conn.cursor() + + # Mandant + cur.execute("SELECT name, level FROM mandant WHERE id = %s", (current_mandant_id,)) + mandant_row = cur.fetchone() + mandant_name = mandant_row[0] + mandant_level_value = mandant_row[1] + + # User + cur.execute(""" + SELECT id, name, email, status + FROM app_user + WHERE mandant_id = %s + """, (current_mandant_id,)) + users = fetchall_dict(cur) + + # Kurse + cur.execute(""" + SELECT id, code, title, min_level + FROM course + WHERE is_active = TRUE + """) + all_courses = fetchall_dict(cur) + + available_courses = [ + c for c in all_courses + if is_course_allowed_for_level(c["code"], mandant_level) + ] + + # Assessments (nur bestanden) + cur.execute(""" + SELECT user_id, course_id, score, created_at + FROM user_assessment + WHERE passed = TRUE + """) + assessments = fetchall_dict(cur) + + cur.close() + conn.close() + + passed_map = {(a["user_id"], a["course_id"]): a for a in assessments} + + detail_rows = [] + completed_count = 0 + open_count = 0 + + for u in users: + for c in available_courses: + a = passed_map.get((u["id"], c["id"])) + done = a is not None + + if done: + completed_count += 1 + else: + open_count += 1 + + detail_rows.append({ + "user_name": u["name"], + "user_email": u["email"], + "course_code": c["code"], + "course_title": c["title"], + "done": done, + "done_label": "erledigt" if done else "offen", + "completed_at": a["created_at"] if done else None, + "score": a["score"] if done else None, + }) + + total_expected = len(users) * len(available_courses) + progress_percent = int((completed_count / total_expected) * 100) if total_expected else 0 + + users = sorted(users, key=lambda u: (u["name"] or "", u["email"] or "")) + available_courses = sorted(available_courses, key=lambda c: c["code"]) + + user_list = sorted( + [{"id": u["id"], "name": u["name"]} for u in users], + key=lambda x: x["name"] or "" + ) + + return render_template( + "reporting_assessments.html", + mandant_name=mandant_name, + mandant_level_label=format_level(mandant_level_value), + available_courses=available_courses, + user_list=user_list, + detail_rows=detail_rows, + dashboard_completed=completed_count, + dashboard_open=open_count, + dashboard_total_expected=total_expected, + dashboard_progress_percent=progress_percent, + **get_current_user() ) \ No newline at end of file diff --git a/app/flask-postgres/app/templates/base.html b/app/flask-postgres/app/templates/base.html index b22e8de..e6201e5 100644 --- a/app/flask-postgres/app/templates/base.html +++ b/app/flask-postgres/app/templates/base.html @@ -42,6 +42,7 @@ {% endif %} {% if is_user_admin %} Useradministration + Reporting {% endif %} {% if is_contentmanager %} Dokumente diff --git a/app/flask-postgres/app/templates/reporting_assessments.html b/app/flask-postgres/app/templates/reporting_assessments.html new file mode 100644 index 0000000..822aa57 --- /dev/null +++ b/app/flask-postgres/app/templates/reporting_assessments.html @@ -0,0 +1,112 @@ +{% extends "base.html" %} + +{% block content %} + +

Reporting

+

Mandant: {{ mandant_name }} ({{ mandant_level_label }})

+ + +
+
+
Erledigt
+
{{ dashboard_completed }}
+
+ +
+
Offen
+
{{ dashboard_open }}
+
+
+ + +
+
+
+ + +
+ + + + + + + + + + +
+ + + + + + + + + + + + + {% for r in detail_rows %} + + + + + + + {% endfor %} + +
UserKursStatusDatum
{{ r.user_name }}{{ r.course_code }} - {{ r.course_title }}{{ r.done_label }} + {% if r.completed_at %} + {{ r.completed_at.strftime("%d.%m.%Y") }} + {% else %} + - + {% endif %} +
+ + + +{% endblock %} \ No newline at end of file diff --git a/app/flask-postgres/styles/site.css b/app/flask-postgres/styles/site.css index baa1801..8bac8db 100644 --- a/app/flask-postgres/styles/site.css +++ b/app/flask-postgres/styles/site.css @@ -1269,4 +1269,30 @@ button { .profile-value { font-weight: 500; +} + +.dashboard-grid { + display: grid; + grid-template-columns: 1fr 1fr; + gap: 20px; +} + +.dashboard-card { + padding: 16px; + background: #f8fbff; + border: 1px solid #dce3ea; + border-radius: 12px; +} + +.dashboard-value { + font-size: 28px; + font-weight: bold; +} + + +.report-filters { + margin: 20px 0; + display: flex; + gap: 12px; + flex-wrap: wrap; } \ No newline at end of file