From 7a8ee5610987201f0180120cb732a9d63e73c9cd Mon Sep 17 00:00:00 2001 From: Christian Cueni Date: Tue, 18 Jun 2024 15:38:43 +0200 Subject: [PATCH] Add language to request, add language backend tests, add icon --- .../dashboard/statistic/AssignmentList.vue | 2 +- .../dashboard/statistic/AttendanceList.vue | 2 +- .../dashboard/statistic/FeedbackList.vue | 2 +- client/src/services/dashboard.ts | 21 ++- client/src/utils/export.ts | 7 +- server/locale/de/LC_MESSAGES/django.po | 35 +++-- server/locale/fr/LC_MESSAGES/django.mo | Bin 2510 -> 2618 bytes server/locale/fr/LC_MESSAGES/django.po | 36 +++-- server/locale/it/LC_MESSAGES/django.mo | Bin 2420 -> 2527 bytes server/locale/it/LC_MESSAGES/django.po | 36 +++-- server/vbv_lernwelt/assignment/export.py | 22 +-- .../test_assignment_completions_export.py | 136 ++++++++++++++++-- .../services/export_attendance.py | 22 +-- .../tests/test_attendance_export.py | 89 +++++++++++- server/vbv_lernwelt/feedback/export.py | 8 +- .../feedback/tests/test_feedback_export.py | 51 +++++++ .../vbv_lernwelt/static/icons/icon-export.svg | 4 + 17 files changed, 387 insertions(+), 86 deletions(-) create mode 100644 server/vbv_lernwelt/static/icons/icon-export.svg diff --git a/client/src/pages/dashboard/statistic/AssignmentList.vue b/client/src/pages/dashboard/statistic/AssignmentList.vue index 3d72eff4..6e5b528f 100644 --- a/client/src/pages/dashboard/statistic/AssignmentList.vue +++ b/client/src/pages/dashboard/statistic/AssignmentList.vue @@ -49,7 +49,7 @@ async function exportData() { return; } const filteredItems = statisticFilter.value.getFilteredItems(); - await exportDataAsXls(filteredItems, exportCompetenceElements); + await exportDataAsXls(filteredItems, exportCompetenceElements, userStore.language); } diff --git a/client/src/pages/dashboard/statistic/AttendanceList.vue b/client/src/pages/dashboard/statistic/AttendanceList.vue index c8b57ed1..f5369042 100644 --- a/client/src/pages/dashboard/statistic/AttendanceList.vue +++ b/client/src/pages/dashboard/statistic/AttendanceList.vue @@ -36,7 +36,7 @@ async function exportData() { return; } const filteredItems = statisticFilter.value.getFilteredItems(); - await exportDataAsXls(filteredItems, exportAttendance); + await exportDataAsXls(filteredItems, exportAttendance, userStore.language); } diff --git a/client/src/pages/dashboard/statistic/FeedbackList.vue b/client/src/pages/dashboard/statistic/FeedbackList.vue index 1bacde78..efd01dea 100644 --- a/client/src/pages/dashboard/statistic/FeedbackList.vue +++ b/client/src/pages/dashboard/statistic/FeedbackList.vue @@ -27,7 +27,7 @@ async function exportData() { return; } const filteredItems = statisticFilter.value.getFilteredItems(); - await exportDataAsXls(filteredItems, exportFeedback); + await exportDataAsXls(filteredItems, exportFeedback, userStore.language); } diff --git a/client/src/services/dashboard.ts b/client/src/services/dashboard.ts index 82bab340..422bf2dc 100644 --- a/client/src/services/dashboard.ts +++ b/client/src/services/dashboard.ts @@ -195,21 +195,30 @@ export async function fetchOpenTasksCount(courseId: string) { } export async function exportFeedback( - data: XlsExportRequestData + data: XlsExportRequestData, + language: string ): Promise { - return await itPost("/api/dashboard/export/feedback/", data); + return await itPost("/api/dashboard/export/feedback/", data, { + headers: { "Accept-Language": language }, + }); } export async function exportAttendance( - data: XlsExportRequestData + data: XlsExportRequestData, + language: string ): Promise { - return await itPost("/api/dashboard/export/attendance/", data); + return await itPost("/api/dashboard/export/attendance/", data, { + headers: { "Accept-Language": language }, + }); } export async function exportCompetenceElements( - data: XlsExportRequestData + data: XlsExportRequestData, + language: string ): Promise { - return await itPost("/api/dashboard/export/competence_elements/", data); + return await itPost("/api/dashboard/export/competence_elements/", data, { + headers: { "Accept-Language": language }, + }); } export function courseIdForCourseSlug( diff --git a/client/src/utils/export.ts b/client/src/utils/export.ts index 67069455..bf72e286 100644 --- a/client/src/utils/export.ts +++ b/client/src/utils/export.ts @@ -5,15 +5,16 @@ import type { } from "@/types"; interface exportApiCall { - (data: XlsExportRequestData): Promise; + (data: XlsExportRequestData, language: string): Promise; } export async function exportDataAsXls( items: StatisticsFilterItem[], - apiCall: exportApiCall + apiCall: exportApiCall, + language: string ) { const itemIds = extractUniqueIds(items); - const data = await apiCall(itemIds); + const data = await apiCall(itemIds, language); openDataAsXls(data.encoded_data, data.file_name); } diff --git a/server/locale/de/LC_MESSAGES/django.po b/server/locale/de/LC_MESSAGES/django.po index abac3a2b..826c9f35 100644 --- a/server/locale/de/LC_MESSAGES/django.po +++ b/server/locale/de/LC_MESSAGES/django.po @@ -8,7 +8,7 @@ msgid "" msgstr "" "Project-Id-Version: PACKAGE VERSION\n" "Report-Msgid-Bugs-To: \n" -"POT-Creation-Date: 2024-06-17 15:43+0200\n" +"POT-Creation-Date: 2024-06-18 15:24+0200\n" "PO-Revision-Date: YEAR-MO-DA HO:MI+ZONE\n" "Last-Translator: FULL NAME \n" "Language-Team: LANGUAGE \n" @@ -22,16 +22,24 @@ msgstr "" msgid "export_kompetenznachweis_elemente" msgstr "" -#: vbv_lernwelt/assignment/export.py:183 +#: vbv_lernwelt/assignment/export.py:142 +msgid "Resultat" +msgstr "" + +#: vbv_lernwelt/assignment/export.py:143 +msgid "bestanden" +msgstr "" + +#: vbv_lernwelt/assignment/export.py:187 msgid "Bestanden" msgstr "" -#: vbv_lernwelt/assignment/export.py:185 +#: vbv_lernwelt/assignment/export.py:189 msgid "Nicht bestanden" msgstr "" -#: vbv_lernwelt/assignment/export.py:199 vbv_lernwelt/assignment/export.py:202 -#: vbv_lernwelt/assignment/export.py:203 +#: vbv_lernwelt/assignment/export.py:203 vbv_lernwelt/assignment/export.py:206 +#: vbv_lernwelt/assignment/export.py:207 msgid "Keine Daten" msgstr "" @@ -111,28 +119,31 @@ msgstr "" msgid "export_anwesenheit" msgstr "" -#: vbv_lernwelt/course_session/services/export_attendance.py:113 +#: vbv_lernwelt/course_session/services/export_attendance.py:92 +msgid "Anwesenheit" +msgstr "" + +#: vbv_lernwelt/course_session/services/export_attendance.py:116 msgid "Anwesend" msgstr "" -#: vbv_lernwelt/course_session/services/export_attendance.py:113 +#: vbv_lernwelt/course_session/services/export_attendance.py:116 msgid "Nicht anwesend" msgstr "" -#: vbv_lernwelt/course_session/services/export_attendance.py:120 +#: vbv_lernwelt/course_session/services/export_attendance.py:123 msgid "Vorname" msgstr "" -#: vbv_lernwelt/course_session/services/export_attendance.py:121 +#: vbv_lernwelt/course_session/services/export_attendance.py:124 msgid "Nachname" msgstr "" -#: vbv_lernwelt/course_session/services/export_attendance.py:122 +#: vbv_lernwelt/course_session/services/export_attendance.py:125 msgid "Email" msgstr "Email" -#: vbv_lernwelt/course_session/services/export_attendance.py:123 -#: vbv_lernwelt/course_session/services/export_attendance.py:135 +#: vbv_lernwelt/course_session/services/export_attendance.py:126 msgid "Lehrvertragsnummer" msgstr "" diff --git a/server/locale/fr/LC_MESSAGES/django.mo b/server/locale/fr/LC_MESSAGES/django.mo index 43c07650e89e0c7e53ad428649c6dfa12ffc78e3..4bf66c9da0ddbb21cd9b59cb4283183ae62f6714 100644 GIT binary patch delta 730 zcmY+?&nrYx6u|K_&lqMfejC4%pJijAEEE=_k?b_eLP~=tlWD>mDVy0T+0euvAPcN$ zM464UVWViIBxR$Njg5tb@0ocEZgbz~p7-9p_uMn@y8Nsn{#54N5?a08zp#z|=-@(| zH$?Jr0Sj;i-MEUmxPe8uZO6N)?;T(no}kXZLNDH8F+OMS$K{&c;d89U7xdu|YN9@})I^$4H}1kF44~f;7nxz88Sc5V6OK?5 zN+D~=1?q_|kyj;+dXfih##cKoWKtn<9qM}R=)q3pC%s$(7{Cr(!%_B^QwHPcW!5at z;Q*#_9&0&9E3VrfVk7Y#GF3iMPxfu=W!F052GoC1ld7UqxPOg;OT2~)^D-sVY;{2` zmzI+KGYfOTOw(_qlwOeyr4}pcT8+O=H`mH(wMy&!mrcuzPK)1@9nG5I zSYpVCpy*T)EV_t=B2p`Yf*p#3qfjimD~KRY z1|1?UegFk0Cvnh^po4?>zqEoLTz`4eCU?(0^<2@Y4 zN`38O3+sE-7d@ix~Th5?){)EgqV}A{OxmZR$&kx!O33L%4~$gTuO~ z*w6Y3^;ERk7)iZy?*d>zrhadCOb(zGTr2F%yn_4W7E+8K$O(te?!O2DKM_u sWc0fxQm3J#7pJh3+sYMItNX^f`(+FoZX#F-RBuDuZrbvU>UTK(18H40R{#J2 diff --git a/server/locale/fr/LC_MESSAGES/django.po b/server/locale/fr/LC_MESSAGES/django.po index 209a4b6e..d338278e 100644 --- a/server/locale/fr/LC_MESSAGES/django.po +++ b/server/locale/fr/LC_MESSAGES/django.po @@ -8,7 +8,7 @@ msgid "" msgstr "" "Project-Id-Version: PACKAGE VERSION\n" "Report-Msgid-Bugs-To: \n" -"POT-Creation-Date: 2024-06-17 15:43+0200\n" +"POT-Creation-Date: 2024-06-18 15:24+0200\n" "PO-Revision-Date: YEAR-MO-DA HO:MI+ZONE\n" "Last-Translator: FULL NAME \n" "Language-Team: LANGUAGE \n" @@ -22,16 +22,24 @@ msgstr "" msgid "export_kompetenznachweis_elemente" msgstr "export_elements_de_controle" -#: vbv_lernwelt/assignment/export.py:183 +#: vbv_lernwelt/assignment/export.py:142 +msgid "Resultat" +msgstr "Résultats" + +#: vbv_lernwelt/assignment/export.py:143 +msgid "bestanden" +msgstr "réussi" + +#: vbv_lernwelt/assignment/export.py:187 msgid "Bestanden" msgstr "Réussi" -#: vbv_lernwelt/assignment/export.py:185 +#: vbv_lernwelt/assignment/export.py:189 msgid "Nicht bestanden" msgstr "Échoué" -#: vbv_lernwelt/assignment/export.py:199 vbv_lernwelt/assignment/export.py:202 -#: vbv_lernwelt/assignment/export.py:203 +#: vbv_lernwelt/assignment/export.py:203 vbv_lernwelt/assignment/export.py:206 +#: vbv_lernwelt/assignment/export.py:207 msgid "Keine Daten" msgstr "Aucune donnée" @@ -111,28 +119,32 @@ msgstr "" msgid "export_anwesenheit" msgstr "export_presence" -#: vbv_lernwelt/course_session/services/export_attendance.py:113 +#: vbv_lernwelt/course_session/services/export_attendance.py:92 +#| msgid "Anwesend" +msgid "Anwesenheit" +msgstr "Présence" + +#: vbv_lernwelt/course_session/services/export_attendance.py:116 msgid "Anwesend" msgstr "Présent" -#: vbv_lernwelt/course_session/services/export_attendance.py:113 +#: vbv_lernwelt/course_session/services/export_attendance.py:116 msgid "Nicht anwesend" msgstr "Pas présent" -#: vbv_lernwelt/course_session/services/export_attendance.py:120 +#: vbv_lernwelt/course_session/services/export_attendance.py:123 msgid "Vorname" msgstr "Prénom" -#: vbv_lernwelt/course_session/services/export_attendance.py:121 +#: vbv_lernwelt/course_session/services/export_attendance.py:124 msgid "Nachname" msgstr "Nom de famille" -#: vbv_lernwelt/course_session/services/export_attendance.py:122 +#: vbv_lernwelt/course_session/services/export_attendance.py:125 msgid "Email" msgstr "Email" -#: vbv_lernwelt/course_session/services/export_attendance.py:123 -#: vbv_lernwelt/course_session/services/export_attendance.py:135 +#: vbv_lernwelt/course_session/services/export_attendance.py:126 msgid "Lehrvertragsnummer" msgstr "Numéro de contrat d'apprentissage" diff --git a/server/locale/it/LC_MESSAGES/django.mo b/server/locale/it/LC_MESSAGES/django.mo index 0993e981009f21a06a5b10d1af13dc9147f90847..e565176b12302822ffe11e760d3c45cc798eda61 100644 GIT binary patch delta 727 zcmY+?J1j#{7{Kw64|9*Y<(5|c>yf9)MO={dji-JW~ycfNZ+{ZE11qu;rsXkB*y#yl>4u-0q?L8UnVruQ7rt-|wJN2;7{M4W z;uId>0={D}PB@jCLlY(83v9zz4B`(;qCv8hL_#PJ9>EZfW6Y6LYKe(tchjO{3l(M;$ zOinFLz7Ze2Spr%IT^=m&^>6DWkP}l_)nxx66_oEJAaOLcZe)#ALPfom*|L$e3R9Zt zo;9*-rlniOGp$?m#Eq<_rxHf0czb^w}7{KwT=h|xhC|6X4M57{17KtB=c0wX)LJXG}q?$-Hmx!SWp$nrR7j}bK zEOId*1_PskT^IfX3?|>_oP;NR?sM)-d@ zV-90DgJGPPwj$?R*XS$yTu5!Cuw} zs2yKmBVGmT2jtuGh!gmX+E{{@r*H&yL)%!vI~>F@x>&*@rl~LY+zeq&A7Gq0w_`SN z9CbnkWFvmFZAiU;!2tc>Gk);8-*^jlkgcR1nI7M7%xWPWn}!xok~+S>p=0J0{N3tV qYQ#yMhK^oxPNlS7a+cj)qwIYdJ%*PFABNnU$f`FST`=7JSm7Hb^fa&l diff --git a/server/locale/it/LC_MESSAGES/django.po b/server/locale/it/LC_MESSAGES/django.po index bd4d61d5..522df647 100644 --- a/server/locale/it/LC_MESSAGES/django.po +++ b/server/locale/it/LC_MESSAGES/django.po @@ -8,7 +8,7 @@ msgid "" msgstr "" "Project-Id-Version: PACKAGE VERSION\n" "Report-Msgid-Bugs-To: \n" -"POT-Creation-Date: 2024-06-17 15:43+0200\n" +"POT-Creation-Date: 2024-06-18 15:24+0200\n" "PO-Revision-Date: YEAR-MO-DA HO:MI+ZONE\n" "Last-Translator: FULL NAME \n" "Language-Team: LANGUAGE \n" @@ -22,16 +22,24 @@ msgstr "" msgid "export_kompetenznachweis_elemente" msgstr "esportazione_elementi_del_controllo" -#: vbv_lernwelt/assignment/export.py:183 +#: vbv_lernwelt/assignment/export.py:142 +msgid "Resultat" +msgstr "Risultato" + +#: vbv_lernwelt/assignment/export.py:143 +msgid "bestanden" +msgstr "superato" + +#: vbv_lernwelt/assignment/export.py:187 msgid "Bestanden" msgstr "Superato" -#: vbv_lernwelt/assignment/export.py:185 +#: vbv_lernwelt/assignment/export.py:189 msgid "Nicht bestanden" msgstr "Fallito" -#: vbv_lernwelt/assignment/export.py:199 vbv_lernwelt/assignment/export.py:202 -#: vbv_lernwelt/assignment/export.py:203 +#: vbv_lernwelt/assignment/export.py:203 vbv_lernwelt/assignment/export.py:206 +#: vbv_lernwelt/assignment/export.py:207 msgid "Keine Daten" msgstr "Nessun dato" @@ -111,28 +119,32 @@ msgstr "" msgid "export_anwesenheit" msgstr "esportazione_presenza" -#: vbv_lernwelt/course_session/services/export_attendance.py:113 +#: vbv_lernwelt/course_session/services/export_attendance.py:92 +#| msgid "Anwesend" +msgid "Anwesenheit" +msgstr "Presenza" + +#: vbv_lernwelt/course_session/services/export_attendance.py:116 msgid "Anwesend" msgstr "Presente" -#: vbv_lernwelt/course_session/services/export_attendance.py:113 +#: vbv_lernwelt/course_session/services/export_attendance.py:116 msgid "Nicht anwesend" msgstr "Non presente" -#: vbv_lernwelt/course_session/services/export_attendance.py:120 +#: vbv_lernwelt/course_session/services/export_attendance.py:123 msgid "Vorname" msgstr "Nome" -#: vbv_lernwelt/course_session/services/export_attendance.py:121 +#: vbv_lernwelt/course_session/services/export_attendance.py:124 msgid "Nachname" msgstr "Cognome" -#: vbv_lernwelt/course_session/services/export_attendance.py:122 +#: vbv_lernwelt/course_session/services/export_attendance.py:125 msgid "Email" msgstr "E-mail" -#: vbv_lernwelt/course_session/services/export_attendance.py:123 -#: vbv_lernwelt/course_session/services/export_attendance.py:135 +#: vbv_lernwelt/course_session/services/export_attendance.py:126 msgid "Lehrvertragsnummer" msgstr "Numero di contratto di tirocinio" diff --git a/server/vbv_lernwelt/assignment/export.py b/server/vbv_lernwelt/assignment/export.py index f8cbc205..230cb661 100644 --- a/server/vbv_lernwelt/assignment/export.py +++ b/server/vbv_lernwelt/assignment/export.py @@ -2,7 +2,7 @@ from dataclasses import dataclass from io import BytesIO import structlog -from django.utils.translation import gettext as _ +from django.utils.translation import gettext_lazy as _ from openpyxl import Workbook from vbv_lernwelt.assignment.models import ( @@ -138,16 +138,20 @@ def _create_sheet( col_prefix = f'Circle "{circle.title}" {cse.learning_content.title}' + # add translation strings here as they are not picked up in f-strings + result_str = str(_("Resultat")) + success_str = str(_("bestanden")) + sheet.cell( row=1, column=col_idx, - value=f"{col_prefix} {_('bestanden')}", + value=f"{col_prefix} {success_str}", ) sheet.cell( row=1, column=col_idx + 1, - value=f"{col_prefix} {_('Resultat')} %", + value=f"{col_prefix} {result_str} %", ) ordered_assignement_ids.append(cse.assignment.id) @@ -180,9 +184,9 @@ def _add_rows( if user_ac: status_text = ( - _("Bestanden") + str(_("Bestanden")) if user_ac.evaluation_passed - else _("Nicht bestanden") + else str(_("Nicht bestanden")) ) sheet.cell(row=row_idx, column=col_idx, value=status_text) try: @@ -196,11 +200,13 @@ def _add_rows( ), ) except (ZeroDivisionError, TypeError): - sheet.cell(row=row_idx, column=col_idx + 1, value=_("Keine Daten")) + sheet.cell( + row=row_idx, column=col_idx + 1, value=str(_("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")) + sheet.cell(row=row_idx, column=col_idx, value=str(_("Keine Daten"))) + sheet.cell(row=row_idx, column=col_idx + 1, value=str(_("Keine Daten"))) col_idx += 2 diff --git a/server/vbv_lernwelt/assignment/tests/test_assignment_completions_export.py b/server/vbv_lernwelt/assignment/tests/test_assignment_completions_export.py index 550bf67f..95528bcc 100644 --- a/server/vbv_lernwelt/assignment/tests/test_assignment_completions_export.py +++ b/server/vbv_lernwelt/assignment/tests/test_assignment_completions_export.py @@ -1,5 +1,6 @@ import io +from django.utils.translation import activate from openpyxl import load_workbook from vbv_lernwelt.assignment.export import export_competence_elements @@ -50,7 +51,7 @@ class AssignmentCompletionExportTestCase(ExportBaseTestCase): self.test_student2.additional_json_data = {"Lehrvertragsnummer": 1987654321} self.test_student2.save() - test_student3 = User.objects.get(email="test-student3@example.com") + self.test_student3 = User.objects.get(email="test-student3@example.com") # Bern assignments update_assignment_completion( @@ -102,9 +103,9 @@ class AssignmentCompletionExportTestCase(ExportBaseTestCase): "Keine Daten", ], [ - test_student3.first_name, - test_student3.last_name, - test_student3.email, + self.test_student3.first_name, + self.test_student3.last_name, + self.test_student3.email, None, "Keine Daten", "Keine Daten", @@ -122,15 +123,7 @@ class AssignmentCompletionExportTestCase(ExportBaseTestCase): def _make_header( self, ): - casework_assignment = CourseSessionAssignment.objects.filter( - course_session__id=self.course_session_be.id, - learning_content__content_assignment__competence_certificate__isnull=False, - ).first() - - edoniq_assignment = CourseSessionEdoniqTest.objects.filter( - course_session__id=self.course_session_be.id, - learning_content__content_assignment__competence_certificate__isnull=False, - ).first() + casework_assignment, edoniq_assignment = self._get_assignments() return [ "Vorname", @@ -143,6 +136,19 @@ class AssignmentCompletionExportTestCase(ExportBaseTestCase): f'Circle "{self.casework.get_attached_circle_title()}" {casework_assignment.learning_content.title} Resultat %', ] + def _get_assignments(self): + casework_assignment = CourseSessionAssignment.objects.filter( + course_session__id=self.course_session_be.id, + learning_content__content_assignment__competence_certificate__isnull=False, + ).first() + + edoniq_assignment = CourseSessionEdoniqTest.objects.filter( + course_session__id=self.course_session_be.id, + learning_content__content_assignment__competence_certificate__isnull=False, + ).first() + + return casework_assignment, edoniq_assignment + def test_export_single_cs(self): wb = self._generate_workbook([self.course_session_be.id]) self.assertEqual(len(wb.sheetnames), 1) @@ -212,3 +218,107 @@ class AssignmentCompletionExportTestCase(ExportBaseTestCase): wb.active = wb["Test Zürich 2022 a"] self._check_export(wb, expected_data, 2, 5) + + def test_french_export(self): + activate("fr") + wb = self._generate_workbook([self.course_session_be.id]) + + casework_assignment, edoniq_assignment = self._get_assignments() + + header = [ + "Prénom", + "Nom de famille", + "E-mail", + "Numéro de contrat d'apprentissage", + f'Circle "{self.edoniq_test.get_attached_circle_title()}" {edoniq_assignment.learning_content.title} réussi', + f'Circle "{self.edoniq_test.get_attached_circle_title()}" {edoniq_assignment.learning_content.title} Résultats %', + f'Circle "{self.casework.get_attached_circle_title()}" {casework_assignment.learning_content.title} réussi', + f'Circle "{self.casework.get_attached_circle_title()}" {casework_assignment.learning_content.title} Résultats %', + ] + + expected_data_be = [ + header, + [ + self.test_student1.first_name, + self.test_student1.last_name, + self.test_student1.email, + self.test_student1.additional_json_data["Lehrvertragsnummer"], + "Échoué", + 58, + "Réussi", + 83, + ], + [ + self.test_student2.first_name, + self.test_student2.last_name, + self.test_student2.email, + self.test_student2.additional_json_data["Lehrvertragsnummer"], + "Réussi", + 100, + "Aucune donnée", + "Aucune donnée", + ], + [ + self.test_student3.first_name, + self.test_student3.last_name, + self.test_student3.email, + None, + "Aucune donnée", + "Aucune donnée", + "Aucune donnée", + "Aucune donnée", + ], + ] + self._check_export(wb, expected_data_be, 4, 8) + + def test_italian_export(self): + activate("it") + wb = self._generate_workbook([self.course_session_be.id]) + + casework_assignment, edoniq_assignment = self._get_assignments() + + header = [ + "Nome", + "Cognome", + "Email", + "Numero di contratto di tirocinio", + f'Circle "{self.edoniq_test.get_attached_circle_title()}" {edoniq_assignment.learning_content.title} superato', + f'Circle "{self.edoniq_test.get_attached_circle_title()}" {edoniq_assignment.learning_content.title} Risultato %', + f'Circle "{self.casework.get_attached_circle_title()}" {casework_assignment.learning_content.title} superato', + f'Circle "{self.casework.get_attached_circle_title()}" {casework_assignment.learning_content.title} Risultato %', + ] + + expected_data_be = [ + header, + [ + self.test_student1.first_name, + self.test_student1.last_name, + self.test_student1.email, + self.test_student1.additional_json_data["Lehrvertragsnummer"], + "Fallito", + 58, + "Superato", + 83, + ], + [ + self.test_student2.first_name, + self.test_student2.last_name, + self.test_student2.email, + self.test_student2.additional_json_data["Lehrvertragsnummer"], + "Superato", + 100, + "Nessun dato", + "Nessun dato", + ], + [ + self.test_student3.first_name, + self.test_student3.last_name, + self.test_student3.email, + None, + "Nessun dato", + "Nessun dato", + "Nessun dato", + "Nessun dato", + ], + ] + self._check_export(wb, expected_data_be, 4, 8) diff --git a/server/vbv_lernwelt/course_session/services/export_attendance.py b/server/vbv_lernwelt/course_session/services/export_attendance.py index 4ce81de1..71587217 100644 --- a/server/vbv_lernwelt/course_session/services/export_attendance.py +++ b/server/vbv_lernwelt/course_session/services/export_attendance.py @@ -4,7 +4,7 @@ from io import BytesIO from itertools import groupby import structlog -from django.utils.translation import gettext as _ +from django.utils.translation import gettext_lazy as _ from openpyxl import Workbook from vbv_lernwelt.course.models import CourseSessionUser @@ -83,15 +83,18 @@ def _create_sheet( # common user headers..., , status , .. col_idx = add_user_headers(sheet) attendance_data = {} + for course in attendance_courses: circle = course.get_circle() if circle_ids and circle.id not in circle_ids: continue + presence_str = str(_("Anwesenheit")) # f-strings are not picked up by gettext + sheet.cell( row=1, column=col_idx, - value=f"{_('Anwesenheit')} {circle.title} {course.due_date.start.strftime('%d.%m.%Y')}", + value=f"{presence_str} {circle.title} {course.due_date.start.strftime('%d.%m.%Y')}", ) user_dict_map = {d["user_id"]: d for d in course.attendance_user_list} attendance_data[circle.title] = user_dict_map @@ -110,17 +113,18 @@ def _add_rows(sheet, users: list[CourseSessionUser], attendance_data): for key, user_dict_map in attendance_data.items(): user_dict = user_dict_map.get(str(user.user.id), {}) status = user_dict.get("status", "") if user_dict else "" - status_text = _("Anwesend") if status == "PRESENT" else _("Nicht anwesend") + status_text = ( + str(_("Anwesend")) if status == "PRESENT" else str(_("Nicht anwesend")) + ) sheet.cell(row=row_idx, column=col_idx, value=status_text) col_idx += 1 def add_user_headers(sheet): - # todo: translate headers - sheet.cell(row=1, column=1, value=_("Vorname")) - sheet.cell(row=1, column=2, value=_("Nachname")) - sheet.cell(row=1, column=3, value=_("Email")) - sheet.cell(row=1, column=4, value=_("Lehrvertragsnummer")) + sheet.cell(row=1, column=1, value=str(_("Vorname"))) + sheet.cell(row=1, column=2, value=str(_("Nachname"))) + sheet.cell(row=1, column=3, value=str(_("Email"))) + sheet.cell(row=1, column=4, value=str(_("Lehrvertragsnummer"))) return 5 # return the next column index @@ -132,7 +136,7 @@ def add_user_export_data(sheet, user: CourseSessionUser, row_idx: int) -> int: sheet.cell( row=row_idx, column=4, - value=user.user.additional_json_data.get(_("Lehrvertragsnummer"), ""), + value=user.user.additional_json_data.get("Lehrvertragsnummer", ""), ) return 5 # return the next column index diff --git a/server/vbv_lernwelt/course_session/tests/test_attendance_export.py b/server/vbv_lernwelt/course_session/tests/test_attendance_export.py index 242febc3..350ffaa4 100644 --- a/server/vbv_lernwelt/course_session/tests/test_attendance_export.py +++ b/server/vbv_lernwelt/course_session/tests/test_attendance_export.py @@ -1,6 +1,7 @@ import io from django.test import TestCase +from django.utils.translation import activate, deactivate from openpyxl import load_workbook from vbv_lernwelt.core.constants import TEST_STUDENT1_USER_ID, TEST_STUDENT2_USER_ID @@ -19,6 +20,10 @@ class ExportBaseTestCase(TestCase): cell.value, expected_data[row[0].row - 1][row.index(cell)] ) + def tearDown(self): + # Deactivate the language after the test + deactivate() + class AttendanceExportTestCase(ExportBaseTestCase): def setUp(self): @@ -41,7 +46,7 @@ class AttendanceExportTestCase(ExportBaseTestCase): self.test_student2.additional_json_data = {"Lehrvertragsnummer": 1987654321} self.test_student2.save() - test_student3 = User.objects.get(email="test-student3@example.com") + self.test_student3 = User.objects.get(email="test-student3@example.com") self.attendance_course_be.attendance_user_list = [ { "email": self.test_student1.email, @@ -69,9 +74,9 @@ class AttendanceExportTestCase(ExportBaseTestCase): "Nicht anwesend", ], [ - test_student3.first_name, - test_student3.last_name, - test_student3.email, + self.test_student3.first_name, + self.test_student3.last_name, + self.test_student3.email, None, "Nicht anwesend", ], @@ -135,3 +140,79 @@ class AttendanceExportTestCase(ExportBaseTestCase): wb.active = wb["Test Zürich 2022 a"] self._check_export(wb, expected_data_zh, 2, 5) + + def test_french_export(self): + activate("fr") + wb = self._generate_workbook([self.course_session_be.id]) + + header = [ + "Prénom", + "Nom de famille", + "E-mail", + "Numéro de contrat d'apprentissage", + f"Présence {self.attendance_course_be.get_circle().title} {self.attendance_course_be.due_date.start.strftime('%d.%m.%Y')}", + ] + + expected_data_be = [ + header, + [ + self.test_student1.first_name, + self.test_student1.last_name, + self.test_student1.email, + self.test_student1.additional_json_data["Lehrvertragsnummer"], + "Présent", + ], + [ + self.test_student2.first_name, + self.test_student2.last_name, + self.test_student2.email, + self.test_student2.additional_json_data["Lehrvertragsnummer"], + "Pas présent", + ], + [ + self.test_student3.first_name, + self.test_student3.last_name, + self.test_student3.email, + None, + "Pas présent", + ], + ] + self._check_export(wb, expected_data_be, 4, 5) + + def test_italian_export(self): + activate("it") + wb = self._generate_workbook([self.course_session_be.id]) + + header = [ + "Nome", + "Cognome", + "Email", + "Numero di contratto di tirocinio", + f"Presenza {self.attendance_course_be.get_circle().title} {self.attendance_course_be.due_date.start.strftime('%d.%m.%Y')}", + ] + + expected_data_be = [ + header, + [ + self.test_student1.first_name, + self.test_student1.last_name, + self.test_student1.email, + self.test_student1.additional_json_data["Lehrvertragsnummer"], + "Presente", + ], + [ + self.test_student2.first_name, + self.test_student2.last_name, + self.test_student2.email, + self.test_student2.additional_json_data["Lehrvertragsnummer"], + "Non presente", + ], + [ + self.test_student3.first_name, + self.test_student3.last_name, + self.test_student3.email, + None, + "Non presente", + ], + ] + self._check_export(wb, expected_data_be, 4, 5) diff --git a/server/vbv_lernwelt/feedback/export.py b/server/vbv_lernwelt/feedback/export.py index 2dab9cb6..879906a7 100644 --- a/server/vbv_lernwelt/feedback/export.py +++ b/server/vbv_lernwelt/feedback/export.py @@ -5,7 +5,7 @@ from typing import List, Tuple import structlog from django.db.models import QuerySet -from django.utils.translation import gettext as _ +from django.utils.translation import gettext_lazy as _ from openpyxl import Workbook from vbv_lernwelt.course_session.services.export_attendance import ( @@ -147,11 +147,11 @@ def _create_sheet(wb: Workbook, title: str, data: list[FeedbackResponse]): ) # add header - sheet.cell(row=1, column=1, value=_("Durchführung")) - sheet.cell(row=1, column=2, value=_("Datum")) + 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=title) + sheet.cell(row=1, column=col_idx, value=str(title)) _add_rows(sheet, data, question_data) return sheet diff --git a/server/vbv_lernwelt/feedback/tests/test_feedback_export.py b/server/vbv_lernwelt/feedback/tests/test_feedback_export.py index c24a7f93..d9785dac 100644 --- a/server/vbv_lernwelt/feedback/tests/test_feedback_export.py +++ b/server/vbv_lernwelt/feedback/tests/test_feedback_export.py @@ -1,6 +1,7 @@ import datetime import io +from django.utils.translation import activate from openpyxl import load_workbook from vbv_lernwelt.core.constants import TEST_STUDENT1_USER_ID, TEST_STUDENT2_USER_ID @@ -201,3 +202,53 @@ class FeedbackExportTestCase(ExportBaseTestCase): self.assertEqual(wb.sheetnames[0], "Fahrzeug") self._check_export(wb, self.expected_data_fahrzeug, 3, 12) + + def test_french_export(self): + activate("fr") + wb = self._generate_workbook( + [self.course_session_be.id, self.course_session_zh.id] + ) + + header = [ + "Opérations", + "Date", + "Degré de satisfaction au global", + "Degré de réalisation des objectifs", + "As-tu l’impression de bien maîtriser les sujets qui ont été abordés pendant le cours ?", + "Les travaux préparatoires étaient-ils clairs et compréhensibles ?", + "Que penses-tu des compétences techniques de la personne chargée du cours et de sa maîtrise du sujet ?", + "Les questions et les suggestions des participants ont-elles été prises au sérieux et traitées correctement ?", + "Souhaites-tu ajouter quelque chose à l’intention de la personne chargée du cours ?", + "Est-ce que tu recommandes ce cours ?", + "Qu’est-ce qui t’a particulièrement plu ?", + "À ton avis, quels sont les points qui pourraient être améliorés ?", + ] + + self.expected_data_fahrzeug[0] = header + + self._check_export(wb, self.expected_data_fahrzeug, 3, 12) + + def test_italian_export(self): + activate("it") + wb = self._generate_workbook( + [self.course_session_be.id, self.course_session_zh.id] + ) + + header = [ + "Svolgimenti", + "Data", + "Soddisfazione complessiva", + "Raggiungimento complessivo degli obiettivi", + "Come valuti il tuo livello di preparazione sui temi dopo il corso?", + "Gli incarichi di preparazione erano chiari e comprensibili?", + "Come valuti il livello di preparazione sui temi e le competenze specialistiche dell’istruttore/istruttrice del corso?", + "Le domande e i suggerimenti dei/delle partecipanti al corso sono stati accolti e presi sul serio?", + "Cos’altro vorresti ancora dire all’istruttore/istruttrice del corso?", + "Raccomanderesti il corso?", + "Cos’hai apprezzato particolarmente?", + "Dove vedi un potenziale di miglioramento?", + ] + + self.expected_data_fahrzeug[0] = header + + self._check_export(wb, self.expected_data_fahrzeug, 3, 12) diff --git a/server/vbv_lernwelt/static/icons/icon-export.svg b/server/vbv_lernwelt/static/icons/icon-export.svg new file mode 100644 index 00000000..9e90e74d --- /dev/null +++ b/server/vbv_lernwelt/static/icons/icon-export.svg @@ -0,0 +1,4 @@ + + + +