diff --git a/server/vbv_lernwelt/assignment/export.py b/server/vbv_lernwelt/assignment/export.py
new file mode 100644
index 00000000..fe845274
--- /dev/null
+++ b/server/vbv_lernwelt/assignment/export.py
@@ -0,0 +1,227 @@
+from dataclasses import dataclass
+from io import BytesIO
+
+import structlog
+from openpyxl import Workbook
+
+from vbv_lernwelt.assignment.models import (
+ Assignment,
+ AssignmentCompletion,
+ AssignmentType,
+)
+from vbv_lernwelt.course.models import CourseSession, CourseSessionUser
+from vbv_lernwelt.course_session.models import (
+ CourseSessionAssignment,
+ CourseSessionEdoniqTest,
+)
+from vbv_lernwelt.course_session.services.export_attendance import (
+ add_user_export_data,
+ add_user_headers,
+ get_ordered_csus_by_course_session,
+ group_by_session_title,
+ make_export_filename,
+ sanitize_sheet_name,
+)
+from vbv_lernwelt.duedate.models import DueDate
+from vbv_lernwelt.learnpath.models import LearningContent
+
+logger = structlog.get_logger(__name__)
+
+
+@dataclass
+class CompetenceCertificateElement:
+ assignment: Assignment
+ date: DueDate
+ learning_content: LearningContent
+ course_session: CourseSession
+
+
+def export_competence_certificates(
+ course_session_ids: list[str],
+ circle_ids: list[int] = None,
+ save_as_file: bool = False,
+):
+ if len(course_session_ids) == 0:
+ return
+
+ COMPETENCE_ASSIGNMENT_TYPES = [
+ AssignmentType.CASEWORK.value,
+ AssignmentType.EDONIQ_TEST.value,
+ ]
+
+ wb = Workbook()
+ # remove the first sheet is just easier than keeping track of the active sheet
+ wb.remove(wb.active)
+
+ competence_certificate_elements = _get_competence_certificate_elements(
+ course_session_ids
+ )
+
+ assignemnt_completions = AssignmentCompletion.objects.filter(
+ course_session_id__in=course_session_ids,
+ assignment__assignment_type__in=COMPETENCE_ASSIGNMENT_TYPES,
+ ).order_by("course_session", "assignment")
+
+ # group all by the sessions title {session_id1: [...], session_id2: [...], ...}
+ grouped_cs_users = get_ordered_csus_by_course_session(course_session_ids)
+ grouped_cce = group_by_session_title(competence_certificate_elements)
+ grouped_ac = group_by_session_title(assignemnt_completions)
+
+ # create a sheet for each course session
+ for course_session_title, cs_users in grouped_cs_users.items():
+ logger.debug(
+ "export_assignment_completion",
+ data={
+ "course_session": course_session_title,
+ },
+ label="assignment_export",
+ )
+ _create_sheet(
+ wb,
+ course_session_title,
+ cs_users,
+ grouped_cce[course_session_title],
+ grouped_ac[course_session_title],
+ circle_ids,
+ )
+
+ if save_as_file:
+ wb.save(make_export_filename(name="competence_certificate_export"))
+ else:
+ output = BytesIO()
+ wb.save(output)
+
+ output.seek(0)
+ return output.getvalue()
+
+
+def _create_sheet(
+ wb: Workbook,
+ title: str,
+ users: list[CourseSessionUser],
+ competence_certificate_element: list[CompetenceCertificateElement],
+ assignment_completions: list[AssignmentCompletion],
+ circle_ids: list[int],
+):
+ sheet = wb.create_sheet(title=sanitize_sheet_name(title))
+
+ if len(users) == 0:
+ return sheet
+
+ # headers
+ # common user headers, Circle
bestanden, Circle Resultat, ...
+ col_idx = add_user_headers(sheet)
+
+ ordered_assignement_ids = (
+ []
+ ) # keep track of the order of the columns when adding the rows
+ for cse in competence_certificate_element:
+ circle = cse.learning_content.get_circle()
+
+ if circle_ids and circle.id not in circle_ids:
+ continue
+
+ col_prefix = f'Circle "{circle.title}" {cse.learning_content.title} '
+
+ sheet.cell(
+ row=1,
+ column=col_idx,
+ value=f"{col_prefix} bestanden",
+ )
+
+ sheet.cell(
+ row=1,
+ column=col_idx + 1,
+ value=f"{col_prefix} Resultat %",
+ )
+
+ ordered_assignement_ids.append(cse.assignment.id)
+
+ col_idx += 2
+
+ # add rows with user results
+ _add_rows(sheet, users, ordered_assignement_ids, assignment_completions)
+
+ return sheet
+
+
+def _add_rows(
+ sheet,
+ users: list[CourseSessionUser],
+ ordered_assignement_ids,
+ assignment_completions,
+):
+ for row_idx, user in enumerate(users, start=2):
+ col_idx = add_user_export_data(sheet, user, row_idx)
+
+ for assignment_id in ordered_assignement_ids:
+ # get the completion for the user and the assignment
+ user_acs = [
+ ac
+ for ac in assignment_completions
+ if ac.assignment_id == assignment_id and ac.assignment_user == user.user
+ ]
+ user_ac = user_acs[0] if user_acs else None
+
+ if user_ac:
+ status_text = (
+ "Bestanden" if user_ac.evaluation_passed else "Nicht bestanden"
+ )
+ sheet.cell(row=row_idx, column=col_idx, value=status_text)
+ try:
+ sheet.cell(
+ row=row_idx,
+ column=col_idx + 1,
+ value=round(
+ 100
+ * user_ac.evaluation_points
+ / user_ac.evaluation_max_points
+ ),
+ )
+ except (ZeroDivisionError, TypeError):
+ sheet.cell(row=row_idx, column=col_idx + 1, value="Keine Daten")
+
+ else:
+ sheet.cell(row=row_idx, column=col_idx, value="Keine Daten")
+ sheet.cell(row=row_idx, column=col_idx + 1, value="Keine Daten")
+
+ col_idx += 2
+
+
+def _get_competence_certificate_elements(
+ course_session_ids: list[str],
+) -> list[CompetenceCertificateElement]:
+ course_session_assignments = CourseSessionAssignment.objects.filter(
+ course_session__id__in=course_session_ids,
+ learning_content__content_assignment__competence_certificate__isnull=False,
+ ).order_by("course_session", "submission_deadline__start")
+
+ course_session_edoniqtests = CourseSessionEdoniqTest.objects.filter(
+ course_session__id__in=course_session_ids,
+ learning_content__content_assignment__competence_certificate__isnull=False,
+ ).order_by("course_session", "deadline__start")
+
+ cse = [
+ CompetenceCertificateElement(
+ assignment=csa.learning_content.content_assignment,
+ date=csa.submission_deadline,
+ learning_content=csa.learning_content,
+ course_session=csa.course_session,
+ )
+ for csa in course_session_assignments
+ ]
+
+ cse += [
+ CompetenceCertificateElement(
+ assignment=cset.learning_content.content_assignment,
+ date=cset.deadline,
+ learning_content=cset.learning_content,
+ course_session=cset.course_session,
+ )
+ for cset in course_session_edoniqtests
+ ]
+
+ # order by course_session and submission_deadline
+ cse.sort(key=lambda x: (x.course_session.title, x.date.start))
+
+ return cse
diff --git a/server/vbv_lernwelt/assignment/management/commands/export_assignment_completions.py b/server/vbv_lernwelt/assignment/management/commands/export_assignment_completions.py
index e7e5b139..0cd3e291 100644
--- a/server/vbv_lernwelt/assignment/management/commands/export_assignment_completions.py
+++ b/server/vbv_lernwelt/assignment/management/commands/export_assignment_completions.py
@@ -1,7 +1,7 @@
import djclick as click
import structlog
-from vbv_lernwelt.assignment.services import export_competence_certificates
+from vbv_lernwelt.assignment.export import export_competence_certificates
logger = structlog.get_logger(__name__)
diff --git a/server/vbv_lernwelt/assignment/services.py b/server/vbv_lernwelt/assignment/services.py
index bba73612..82cf5110 100644
--- a/server/vbv_lernwelt/assignment/services.py
+++ b/server/vbv_lernwelt/assignment/services.py
@@ -1,10 +1,7 @@
from copy import deepcopy
-from dataclasses import dataclass
-from io import BytesIO
import structlog
from django.utils import timezone
-from openpyxl import Workbook
from rest_framework import serializers
from wagtail.models import Page
@@ -19,39 +16,13 @@ from vbv_lernwelt.assignment.models import (
)
from vbv_lernwelt.core.models import User
from vbv_lernwelt.core.utils import find_first
-from vbv_lernwelt.course.models import (
- CourseCompletionStatus,
- CourseSession,
- CourseSessionUser,
-)
+from vbv_lernwelt.course.models import CourseCompletionStatus, CourseSession
from vbv_lernwelt.course.services import mark_course_completion
-from vbv_lernwelt.course_session.models import (
- CourseSessionAssignment,
- CourseSessionEdoniqTest,
-)
-from vbv_lernwelt.course_session.services.export import (
- add_user_export_data,
- add_user_headers,
- get_ordered_csus_by_course_session,
- group_by_session_title,
- make_export_filename,
- sanitize_sheet_name,
-)
-from vbv_lernwelt.duedate.models import DueDate
-from vbv_lernwelt.learnpath.models import LearningContent
from vbv_lernwelt.notify.services import NotificationService
logger = structlog.get_logger(__name__)
-@dataclass
-class CompetenceCertificateElement:
- assignment: Assignment
- date: DueDate
- learning_content: LearningContent
- course_session: CourseSession
-
-
def update_assignment_completion(
assignment_user: User,
assignment: Assignment,
@@ -300,194 +271,3 @@ def _remove_unknown_entries(assignment, completion_data):
key: value for key, value in completion_data.items() if key in input_task_ids
}
return filtered_completion_data
-
-
-def export_competence_certificates(
- course_session_ids: list[str],
- circle_ids: list[int] = None,
- save_as_file: bool = False,
-):
- if len(course_session_ids) == 0:
- return
-
- COMPETENCE_ASSIGNMENT_TYPES = [
- AssignmentType.CASEWORK.value,
- AssignmentType.EDONIQ_TEST.value,
- ]
-
- wb = Workbook()
- # remove the first sheet is just easier than keeping track of the active sheet
- wb.remove(wb.active)
-
- competence_certificate_elements = _get_competence_certificate_elements(
- course_session_ids
- )
-
- assignemnt_completions = AssignmentCompletion.objects.filter(
- course_session_id__in=course_session_ids,
- assignment__assignment_type__in=COMPETENCE_ASSIGNMENT_TYPES,
- ).order_by("course_session", "assignment")
-
- # group all by the sessions title {session_id1: [...], session_id2: [...], ...}
- grouped_cs_users = get_ordered_csus_by_course_session(course_session_ids)
- grouped_cce = group_by_session_title(competence_certificate_elements)
- grouped_ac = group_by_session_title(assignemnt_completions)
-
- # create a sheet for each course session
- for course_session, cs_users in grouped_cs_users.items():
- logger.debug(
- "export_assignment_completion",
- data={
- "course_session": course_session,
- },
- label="assignment_export",
- )
- _create_sheet(
- wb,
- course_session,
- cs_users,
- grouped_cce[course_session],
- grouped_ac[course_session],
- circle_ids,
- )
-
- if save_as_file:
- wb.save(make_export_filename(name="competence_certificate_export"))
- else:
- output = BytesIO()
- wb.save(output)
-
- output.seek(0)
- return output.getvalue()
-
-
-def _create_sheet(
- wb: Workbook,
- title: str,
- users: list[CourseSessionUser],
- competence_certificate_element: list[CompetenceCertificateElement],
- assignment_completions: list[AssignmentCompletion],
- circle_ids: list[int],
-):
- sheet = wb.create_sheet(title=sanitize_sheet_name(title))
-
- if len(users) == 0:
- return sheet
-
- # headers
- # common user headers, Circle bestanden, Circle Resultat, ...
- col_idx = add_user_headers(sheet)
-
- ordered_assignement_ids = (
- []
- ) # keep track of the order of the columns when adding the rows
- for cse in competence_certificate_element:
- circle = cse.learning_content.get_circle()
-
- if circle_ids and circle.id not in circle_ids:
- continue
-
- col_prefix = f'Circle "{circle.title}" {cse.learning_content.title} '
-
- sheet.cell(
- row=1,
- column=col_idx,
- value=f"{col_prefix} bestanden",
- )
-
- sheet.cell(
- row=1,
- column=col_idx + 1,
- value=f"{col_prefix} Resultat %",
- )
-
- ordered_assignement_ids.append(cse.assignment.id)
-
- col_idx += 2
-
- # add rows with user results
- _add_rows(sheet, users, ordered_assignement_ids, assignment_completions)
-
- return sheet
-
-
-def _add_rows(
- sheet,
- users: list[CourseSessionUser],
- ordered_assignement_ids,
- assignment_completions,
-):
- for row_idx, user in enumerate(users, start=2):
- col_idx = add_user_export_data(sheet, user, row_idx)
-
- for assignment_id in ordered_assignement_ids:
- # get the completion for the user and the assignment
- user_acs = [
- ac
- for ac in assignment_completions
- if ac.assignment_id == assignment_id and ac.assignment_user == user.user
- ]
- user_ac = user_acs[0] if user_acs else None
-
- if user_ac:
- status_text = (
- "Bestanden" if user_ac.evaluation_passed else "Nicht bestanden"
- )
- sheet.cell(row=row_idx, column=col_idx, value=status_text)
- try:
- sheet.cell(
- row=row_idx,
- column=col_idx + 1,
- value=round(
- 100
- * user_ac.evaluation_points
- / user_ac.evaluation_max_points
- ),
- )
- except (ZeroDivisionError, TypeError):
- sheet.cell(row=row_idx, column=col_idx + 1, value="Keine Daten")
-
- else:
- sheet.cell(row=row_idx, column=col_idx, value="Keine Daten")
- sheet.cell(row=row_idx, column=col_idx + 1, value="Keine Daten")
-
- col_idx += 2
-
-
-def _get_competence_certificate_elements(
- course_session_ids: list[str],
-) -> list[CompetenceCertificateElement]:
- course_session_assignments = CourseSessionAssignment.objects.filter(
- course_session__id__in=course_session_ids,
- learning_content__content_assignment__competence_certificate__isnull=False,
- ).order_by("course_session", "submission_deadline__start")
-
- course_session_edoniqtests = CourseSessionEdoniqTest.objects.filter(
- course_session__id__in=course_session_ids,
- learning_content__content_assignment__competence_certificate__isnull=False,
- ).order_by("course_session", "deadline__start")
-
- cse = [
- CompetenceCertificateElement(
- assignment=csa.learning_content.content_assignment,
- date=csa.submission_deadline,
- learning_content=csa.learning_content,
- course_session=csa.course_session,
- )
- for csa in course_session_assignments
- ]
-
- cse += [
- CompetenceCertificateElement(
- assignment=cset.learning_content.content_assignment,
- date=cset.deadline,
- learning_content=cset.learning_content,
- course_session=cset.course_session,
- )
- for cset in course_session_edoniqtests
- ]
-
- # order by course_session and submission_deadline
- cse.sort(key=lambda x: (x.course_session.title, x.date.start))
-
- return cse
diff --git a/server/vbv_lernwelt/assignment/tests/test_assignment_completions_export.py b/server/vbv_lernwelt/assignment/tests/test_assignment_completions_export.py
new file mode 100644
index 00000000..10f6da0f
--- /dev/null
+++ b/server/vbv_lernwelt/assignment/tests/test_assignment_completions_export.py
@@ -0,0 +1,56 @@
+from django.test import TestCase
+
+from vbv_lernwelt.assignment.models import Assignment
+from vbv_lernwelt.assignment.services import update_assignment_completion
+from vbv_lernwelt.core.constants import TEST_STUDENT1_USER_ID, TEST_STUDENT2_USER_ID
+from vbv_lernwelt.core.create_default_users import create_default_users
+from vbv_lernwelt.core.models import User
+from vbv_lernwelt.course.creators.test_course import create_test_course
+from vbv_lernwelt.course.models import CourseSession
+
+
+class AttendanceExportTestCase(TestCase):
+ def setUp(self):
+ create_default_users()
+ self.course = create_test_course(include_vv=False, with_sessions=True)
+ self.course_session_be = CourseSession.objects.get(title="Test Bern 2022 a")
+ self.course_session_zh = CourseSession.objects.get(title="Test Zürich 2022 a")
+ self.attendance_course_be = (
+ self.course_session_be.coursesessionattendancecourse_set.first()
+ )
+
+ some = (
+ self.course.coursepage.get_descendants()
+ .exact_type(Assignment)
+ .filter(assignment__assignment_type="CASEWORK")
+ )
+
+ self.assignment = (
+ self.course.coursepage.get_descendants()
+ .exact_type(Assignment)
+ .filter(assignment__assignment_type="CASEWORK")
+ .first()
+ .specific
+ )
+
+ self.trainer = User.objects.get(username="admin")
+
+ self.test_student1 = User.objects.get(id=TEST_STUDENT1_USER_ID)
+ self.test_student1.additional_json_data = {"Lehrvertragsnummer": 1234567890}
+ self.test_student1.save()
+ self.test_student2 = User.objects.get(id=TEST_STUDENT2_USER_ID)
+ self.test_student2.additional_json_data = {"Lehrvertragsnummer": 1987654321}
+ self.test_student2.save()
+
+ test_student3 = User.objects.get(email="test-student3@example.com")
+
+ update_assignment_completion(
+ assignment_user=self.test_student1,
+ assignment=self.assignment,
+ course_session=self.course_session_be,
+ completion_data={},
+ evaluation_points=20,
+ )
+
+ def test_attendance_export_single_cs(self):
+ self.assertTrue(True)
diff --git a/server/vbv_lernwelt/course_session/management/commands/export_attendance.py b/server/vbv_lernwelt/course_session/management/commands/export_attendance.py
index 4ab8560a..4854e783 100644
--- a/server/vbv_lernwelt/course_session/management/commands/export_attendance.py
+++ b/server/vbv_lernwelt/course_session/management/commands/export_attendance.py
@@ -1,7 +1,7 @@
import djclick as click
import structlog
-from vbv_lernwelt.course_session.services.export import export_attendance
+from vbv_lernwelt.course_session.services.export_attendance import export_attendance
logger = structlog.get_logger(__name__)
diff --git a/server/vbv_lernwelt/course_session/services/export.py b/server/vbv_lernwelt/course_session/services/export_attendance.py
similarity index 97%
rename from server/vbv_lernwelt/course_session/services/export.py
rename to server/vbv_lernwelt/course_session/services/export_attendance.py
index 5c6d9e26..92ec69f2 100644
--- a/server/vbv_lernwelt/course_session/services/export.py
+++ b/server/vbv_lernwelt/course_session/services/export_attendance.py
@@ -28,7 +28,7 @@ def export_attendance(
grouped_cs_users = get_ordered_csus_by_course_session(course_session_ids)
- # create dict with course_session as key and list of attendance_courses as value. Easier to access in the loop
+ # create dict with course_session_title as key and list of attendance_courses as value. Easier to access in the loop
grouped_attendance_course = {
key: list(group)
for key, group in groupby(
diff --git a/server/vbv_lernwelt/course_session/tests/test_attendance_export.py b/server/vbv_lernwelt/course_session/tests/test_attendance_export.py
new file mode 100644
index 00000000..49f1c085
--- /dev/null
+++ b/server/vbv_lernwelt/course_session/tests/test_attendance_export.py
@@ -0,0 +1,135 @@
+import io
+
+from django.test import TestCase
+from openpyxl import load_workbook
+
+from vbv_lernwelt.core.constants import TEST_STUDENT1_USER_ID, TEST_STUDENT2_USER_ID
+from vbv_lernwelt.core.create_default_users import create_default_users
+from vbv_lernwelt.core.models import User
+from vbv_lernwelt.course.creators.test_course import create_test_course
+from vbv_lernwelt.course.models import CourseSession
+from vbv_lernwelt.course_session.services.export_attendance import export_attendance
+
+
+class AttendanceExportTestCase(TestCase):
+ def setUp(self):
+ create_default_users()
+ create_test_course(include_vv=False, with_sessions=True)
+ self.course_session_be = CourseSession.objects.get(title="Test Bern 2022 a")
+ self.course_session_zh = CourseSession.objects.get(title="Test Zürich 2022 a")
+ self.attendance_course_be = (
+ self.course_session_be.coursesessionattendancecourse_set.first()
+ )
+
+ self.attendance_course_zh = (
+ self.course_session_zh.coursesessionattendancecourse_set.first()
+ )
+
+ self.test_student1 = User.objects.get(id=TEST_STUDENT1_USER_ID)
+ self.test_student1.additional_json_data = {"Lehrvertragsnummer": 1234567890}
+ self.test_student1.save()
+ self.test_student2 = User.objects.get(id=TEST_STUDENT2_USER_ID)
+ self.test_student2.additional_json_data = {"Lehrvertragsnummer": 1987654321}
+ self.test_student2.save()
+
+ test_student3 = User.objects.get(email="test-student3@example.com")
+ self.attendance_course_be.attendance_user_list = [
+ {
+ "email": self.test_student1.email,
+ "status": "PRESENT",
+ "user_id": str(self.test_student1.id),
+ "last_name": self.test_student1.last_name,
+ "first_name": self.test_student1.first_name,
+ }
+ ]
+
+ self.expected_data_be = [
+ self._make_header(self.attendance_course_be),
+ [
+ self.test_student1.first_name,
+ self.test_student1.last_name,
+ self.test_student1.email,
+ self.test_student1.additional_json_data["Lehrvertragsnummer"],
+ "Anwesend",
+ ],
+ [
+ self.test_student2.first_name,
+ self.test_student2.last_name,
+ self.test_student2.email,
+ self.test_student2.additional_json_data["Lehrvertragsnummer"],
+ "Nicht anwesend",
+ ],
+ [
+ test_student3.first_name,
+ test_student3.last_name,
+ test_student3.email,
+ None,
+ "Nicht anwesend",
+ ],
+ ]
+ self.attendance_course_be.save()
+
+ def _generate_workbook(self, course_session_ids):
+ export_data = io.BytesIO(
+ export_attendance(course_session_ids, save_as_file=False)
+ )
+ return load_workbook(export_data)
+
+ def _make_header(self, csac):
+ return (
+ [
+ "Vorname",
+ "Nachname",
+ "Email",
+ "Lehrvertragsnummer",
+ f"Anwesenheit {csac.get_circle().title} {csac.attendance_course_zh.due_date.start.strftime('%d.%m.%Y')}",
+ ],
+ )
+
+ def _check_attendance_export(self, wb, expected_data, max_row, max_col):
+ for row in wb.active.iter_rows(max_col=max_col, max_row=max_row):
+ for cell in row:
+ self.assertEqual(
+ cell.value, expected_data[row[0].row - 1][row.index(cell)]
+ )
+
+ def test_attendance_export_single_cs(self):
+ wb = self._generate_workbook([self.course_session_be.id])
+ self.assertEqual(len(wb.sheetnames), 1)
+ self.assertEqual(wb.sheetnames[0], "Test Bern 2022 a")
+
+ self._check_attendance_export(wb, self.expected_data_be, 4, 5)
+
+ def test_attendance_export_multiple_cs(self):
+ self.attendance_course_zh.attendance_user_list = [
+ {
+ "email": self.test_student2.email,
+ "status": "PRESENT",
+ "user_id": str(self.test_student2.id),
+ "last_name": self.test_student2.last_name,
+ "first_name": self.test_student2.first_name,
+ }
+ ]
+
+ expected_data_zh = [
+ self._make_header(self.attendance_course_zh),
+ [
+ self.test_student2.first_name,
+ self.test_student2.last_name,
+ self.test_student2.email,
+ self.test_student2.additional_json_data["Lehrvertragsnummer"],
+ "Anwesend",
+ ],
+ ]
+
+ self.attendance_course_zh.save()
+
+ wb = self._generate_workbook(
+ [self.course_session_be.id, self.course_session_zh.id]
+ )
+ self.assertEqual(len(wb.sheetnames), 2)
+ self.assertEqual(wb.sheetnames[0], "Test Bern 2022 a")
+ self.assertEqual(wb.sheetnames[1], "Test Zürich 2022 a")
+
+ wb.active = wb["Test Zürich 2022 a"]
+ self._check_attendance_export(wb, expected_data_zh, 2, 5)
diff --git a/server/vbv_lernwelt/dashboard/views.py b/server/vbv_lernwelt/dashboard/views.py
index e28d474a..5f660a0e 100644
--- a/server/vbv_lernwelt/dashboard/views.py
+++ b/server/vbv_lernwelt/dashboard/views.py
@@ -9,11 +9,11 @@ from rest_framework.decorators import api_view
from rest_framework.exceptions import PermissionDenied
from rest_framework.response import Response
+from vbv_lernwelt.assignment.export import export_competence_certificates
from vbv_lernwelt.assignment.models import (
AssignmentCompletion,
AssignmentCompletionStatus,
)
-from vbv_lernwelt.assignment.services import export_competence_certificates
from vbv_lernwelt.competence.services import (
query_competence_course_session_assignments,
query_competence_course_session_edoniq_tests,
@@ -25,7 +25,7 @@ from vbv_lernwelt.course.models import (
CourseSessionUser,
)
from vbv_lernwelt.course.views import logger
-from vbv_lernwelt.course_session.services.export import (
+from vbv_lernwelt.course_session.services.export_attendance import (
export_attendance,
make_export_filename,
)
diff --git a/server/vbv_lernwelt/feedback/services.py b/server/vbv_lernwelt/feedback/services.py
index 57422043..e4b49cfd 100644
--- a/server/vbv_lernwelt/feedback/services.py
+++ b/server/vbv_lernwelt/feedback/services.py
@@ -10,7 +10,7 @@ from openpyxl import Workbook
from vbv_lernwelt.core.models import User
from vbv_lernwelt.course.models import CourseCompletionStatus, CourseSession
from vbv_lernwelt.course.services import mark_course_completion
-from vbv_lernwelt.course_session.services.export import (
+from vbv_lernwelt.course_session.services.export_attendance import (
make_export_filename,
sanitize_sheet_name,
)