169 lines
5.6 KiB
Python
169 lines
5.6 KiB
Python
from io import BytesIO
|
|
from itertools import groupby
|
|
from operator import attrgetter
|
|
from typing import List, Tuple
|
|
|
|
import structlog
|
|
from django.db.models import QuerySet
|
|
from django.utils.translation import gettext_lazy as _
|
|
from openpyxl import Workbook
|
|
|
|
from vbv_lernwelt.course_session.services.export_attendance import (
|
|
make_export_filename,
|
|
sanitize_sheet_name,
|
|
)
|
|
from vbv_lernwelt.feedback.models import FeedbackResponse
|
|
|
|
logger = structlog.get_logger(__name__)
|
|
|
|
FEEDBACK_EXPORT_FILE_NAME = _("export_feedback")
|
|
|
|
VV_FEEDBACK_QUESTIONS = [
|
|
("satisfaction", "Zufriedenheit insgesamt"),
|
|
("goal_attainment", "Zielerreichung insgesamt"),
|
|
(
|
|
"proficiency",
|
|
"Wie beurteilst du deine Sicherheit bezüglichen den Themen nach dem Circle?",
|
|
),
|
|
("preparation_task_clarity", "Waren die Praxisaufträge klar und verständlich?"),
|
|
("would_recommend", "Würdest du den Circle weiterempfehlen?"),
|
|
("course_positive_feedback", "Was hat dir besonders gut gefallen?"),
|
|
("course_negative_feedback", "Wo siehst du Verbesserungspotential?"),
|
|
]
|
|
|
|
UK_FEEDBACK_QUESTIONS = [
|
|
("satisfaction", _("Zufriedenheit insgesamt")),
|
|
("goal_attainment", _("Zielerreichung insgesamt")),
|
|
(
|
|
"proficiency",
|
|
_("Wie beurteilst du deine Sicherheit bezüglichen den Themen nach dem Kurs?"),
|
|
),
|
|
(
|
|
"preparation_task_clarity",
|
|
_("Waren die Vorbereitungsaufträge klar und verständlich?"),
|
|
),
|
|
(
|
|
"instructor_competence",
|
|
_(
|
|
"Wie beurteilst du die Themensicherheit und Fachkompetenz des Kursleiters/der Kursleiterin?"
|
|
),
|
|
),
|
|
(
|
|
"instructor_respect",
|
|
_(
|
|
"Wurden Fragen und Anregungen der Kursteilnehmenden ernst genommen und aufgegriffen?"
|
|
),
|
|
),
|
|
(
|
|
"instructor_open_feedback",
|
|
_("Was möchtest du dem Kursleiter/der Kursleiterin sonst noch sagen?"),
|
|
),
|
|
("would_recommend", _("Würdest du den Kurs weiterempfehlen?")),
|
|
("course_positive_feedback", _("Was hat dir besonders gut gefallen?")),
|
|
("course_negative_feedback", _("Wo siehst du Verbesserungspotential?")),
|
|
]
|
|
|
|
|
|
def export_feedback(course_session_ids: list[str], save_as_file: bool, circles=None):
|
|
"""
|
|
Export for django view, all circles are allowed
|
|
"""
|
|
if circles:
|
|
feedback_unordered = FeedbackResponse.objects.filter(
|
|
course_session_id__in=course_session_ids,
|
|
circle__in=circles,
|
|
submitted=True,
|
|
)
|
|
else:
|
|
feedback_unordered = FeedbackResponse.objects.filter(
|
|
course_session_id__in=course_session_ids,
|
|
submitted=True,
|
|
)
|
|
|
|
return _generate_feedback_export(feedback_unordered, save_as_file)
|
|
|
|
|
|
def export_feedback_with_circle_restriction(
|
|
course_sessions_with_circles: List[Tuple[int, List[int]]], save_as_file: bool
|
|
):
|
|
"""
|
|
Export for user export, only circles in specified course sessions are allowed
|
|
"""
|
|
feedback_unordered = FeedbackResponse.objects.none()
|
|
|
|
for course_session_with_circles in course_sessions_with_circles:
|
|
feedback_unordered = feedback_unordered | FeedbackResponse.objects.filter(
|
|
course_session_id=course_session_with_circles[0],
|
|
circle_id__in=course_session_with_circles[1],
|
|
submitted=True,
|
|
)
|
|
|
|
return _generate_feedback_export(feedback_unordered, save_as_file)
|
|
|
|
|
|
def _generate_feedback_export(feedback_unordered: QuerySet, save_as_file: bool):
|
|
wb = Workbook()
|
|
|
|
# remove the first sheet is just easier than keeping track of the active sheet
|
|
wb.remove(wb.active)
|
|
|
|
feedbacks = feedback_unordered.order_by("circle", "course_session", "updated_at")
|
|
grouped_feedbacks = groupby(feedbacks, key=attrgetter("circle"))
|
|
|
|
for circle, group_feedbacks in grouped_feedbacks:
|
|
group_feedbacks = list(group_feedbacks)
|
|
logger.debug(
|
|
"export_feedback_for_circle",
|
|
data={
|
|
"circle": circle.id,
|
|
"count": len(group_feedbacks),
|
|
},
|
|
label="feedback_export",
|
|
)
|
|
_create_sheet(wb, circle.title, group_feedbacks)
|
|
|
|
if save_as_file:
|
|
wb.save(make_export_filename(FEEDBACK_EXPORT_FILE_NAME))
|
|
else:
|
|
# todo handle IndexError
|
|
output = BytesIO()
|
|
wb.save(output)
|
|
|
|
output.seek(0)
|
|
return output.getvalue()
|
|
|
|
|
|
def _create_sheet(wb: Workbook, title: str, data: list[FeedbackResponse]):
|
|
sheet = wb.create_sheet(title=sanitize_sheet_name(title))
|
|
|
|
if len(data) == 0:
|
|
return sheet
|
|
|
|
# we instruct the users not to mix exports of different courses, so we can assume the questions are the same and of the first type
|
|
question_data = (
|
|
UK_FEEDBACK_QUESTIONS
|
|
if data[0].data["feedback_type"] == "uk"
|
|
else VV_FEEDBACK_QUESTIONS
|
|
)
|
|
|
|
# add header
|
|
sheet.cell(row=1, column=1, value=str(_("Durchführung")))
|
|
sheet.cell(row=1, column=2, value=str(_("Datum")))
|
|
questions = [q[1] for q in question_data]
|
|
for col_idx, title in enumerate(questions, start=3):
|
|
sheet.cell(row=1, column=col_idx, value=str(title))
|
|
|
|
_add_rows(sheet, data, question_data)
|
|
return sheet
|
|
|
|
|
|
def _add_rows(sheet, data, question_data):
|
|
for row_idx, feedback in enumerate(data, start=2):
|
|
sheet.cell(row=row_idx, column=1, value=feedback.course_session.title)
|
|
sheet.cell(
|
|
row=row_idx, column=2, value=feedback.updated_at.date().strftime("%d.%m.%Y")
|
|
)
|
|
for col_idx, question in enumerate(question_data, start=3):
|
|
response = feedback.data.get(question[0], "")
|
|
sheet.cell(row=row_idx, column=col_idx, value=response)
|