wip: Split up code, add attendance tests [skip ci]
This commit is contained in:
parent
0cad9666c5
commit
54d77264cb
|
|
@ -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 <title> <learningcontenttitle> bestanden, Circle <title> <learningcontenttitle> 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
|
||||||
|
|
@ -1,7 +1,7 @@
|
||||||
import djclick as click
|
import djclick as click
|
||||||
import structlog
|
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__)
|
logger = structlog.get_logger(__name__)
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -1,10 +1,7 @@
|
||||||
from copy import deepcopy
|
from copy import deepcopy
|
||||||
from dataclasses import dataclass
|
|
||||||
from io import BytesIO
|
|
||||||
|
|
||||||
import structlog
|
import structlog
|
||||||
from django.utils import timezone
|
from django.utils import timezone
|
||||||
from openpyxl import Workbook
|
|
||||||
from rest_framework import serializers
|
from rest_framework import serializers
|
||||||
from wagtail.models import Page
|
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.models import User
|
||||||
from vbv_lernwelt.core.utils import find_first
|
from vbv_lernwelt.core.utils import find_first
|
||||||
from vbv_lernwelt.course.models import (
|
from vbv_lernwelt.course.models import CourseCompletionStatus, CourseSession
|
||||||
CourseCompletionStatus,
|
|
||||||
CourseSession,
|
|
||||||
CourseSessionUser,
|
|
||||||
)
|
|
||||||
from vbv_lernwelt.course.services import mark_course_completion
|
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
|
from vbv_lernwelt.notify.services import NotificationService
|
||||||
|
|
||||||
logger = structlog.get_logger(__name__)
|
logger = structlog.get_logger(__name__)
|
||||||
|
|
||||||
|
|
||||||
@dataclass
|
|
||||||
class CompetenceCertificateElement:
|
|
||||||
assignment: Assignment
|
|
||||||
date: DueDate
|
|
||||||
learning_content: LearningContent
|
|
||||||
course_session: CourseSession
|
|
||||||
|
|
||||||
|
|
||||||
def update_assignment_completion(
|
def update_assignment_completion(
|
||||||
assignment_user: User,
|
assignment_user: User,
|
||||||
assignment: Assignment,
|
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
|
key: value for key, value in completion_data.items() if key in input_task_ids
|
||||||
}
|
}
|
||||||
return filtered_completion_data
|
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 <title> <learningcontenttitle> bestanden, Circle <title> <learningcontenttitle> 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
|
|
||||||
|
|
|
||||||
|
|
@ -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)
|
||||||
|
|
@ -1,7 +1,7 @@
|
||||||
import djclick as click
|
import djclick as click
|
||||||
import structlog
|
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__)
|
logger = structlog.get_logger(__name__)
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -28,7 +28,7 @@ def export_attendance(
|
||||||
|
|
||||||
grouped_cs_users = get_ordered_csus_by_course_session(course_session_ids)
|
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 = {
|
grouped_attendance_course = {
|
||||||
key: list(group)
|
key: list(group)
|
||||||
for key, group in groupby(
|
for key, group in groupby(
|
||||||
|
|
@ -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)
|
||||||
|
|
@ -9,11 +9,11 @@ from rest_framework.decorators import api_view
|
||||||
from rest_framework.exceptions import PermissionDenied
|
from rest_framework.exceptions import PermissionDenied
|
||||||
from rest_framework.response import Response
|
from rest_framework.response import Response
|
||||||
|
|
||||||
|
from vbv_lernwelt.assignment.export import export_competence_certificates
|
||||||
from vbv_lernwelt.assignment.models import (
|
from vbv_lernwelt.assignment.models import (
|
||||||
AssignmentCompletion,
|
AssignmentCompletion,
|
||||||
AssignmentCompletionStatus,
|
AssignmentCompletionStatus,
|
||||||
)
|
)
|
||||||
from vbv_lernwelt.assignment.services import export_competence_certificates
|
|
||||||
from vbv_lernwelt.competence.services import (
|
from vbv_lernwelt.competence.services import (
|
||||||
query_competence_course_session_assignments,
|
query_competence_course_session_assignments,
|
||||||
query_competence_course_session_edoniq_tests,
|
query_competence_course_session_edoniq_tests,
|
||||||
|
|
@ -25,7 +25,7 @@ from vbv_lernwelt.course.models import (
|
||||||
CourseSessionUser,
|
CourseSessionUser,
|
||||||
)
|
)
|
||||||
from vbv_lernwelt.course.views import logger
|
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,
|
export_attendance,
|
||||||
make_export_filename,
|
make_export_filename,
|
||||||
)
|
)
|
||||||
|
|
|
||||||
|
|
@ -10,7 +10,7 @@ from openpyxl import Workbook
|
||||||
from vbv_lernwelt.core.models import User
|
from vbv_lernwelt.core.models import User
|
||||||
from vbv_lernwelt.course.models import CourseCompletionStatus, CourseSession
|
from vbv_lernwelt.course.models import CourseCompletionStatus, CourseSession
|
||||||
from vbv_lernwelt.course.services import mark_course_completion
|
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,
|
make_export_filename,
|
||||||
sanitize_sheet_name,
|
sanitize_sheet_name,
|
||||||
)
|
)
|
||||||
|
|
|
||||||
Loading…
Reference in New Issue