Merged in feature/VBV-696-person-export (pull request #355)
Feature/VBV-696 person export Approved-by: Elia Bieri
This commit is contained in:
commit
8f0c68270b
|
|
@ -6,9 +6,14 @@ import ItDropdownSelect from "@/components/ui/ItDropdownSelect.vue";
|
||||||
import { computed, ref, watch } from "vue";
|
import { computed, ref, watch } from "vue";
|
||||||
import { useTranslation } from "i18next-vue";
|
import { useTranslation } from "i18next-vue";
|
||||||
import _ from "lodash";
|
import _ from "lodash";
|
||||||
import type { DashboardPersonCourseSessionType } from "@/services/dashboard";
|
import {
|
||||||
|
type DashboardPersonCourseSessionType,
|
||||||
|
exportPersons,
|
||||||
|
} from "@/services/dashboard";
|
||||||
import { useRouteQuery } from "@vueuse/router";
|
import { useRouteQuery } from "@vueuse/router";
|
||||||
import type { DashboardPersonsPageMode } from "@/types";
|
import type { DashboardPersonsPageMode, StatisticsFilterItem } from "@/types";
|
||||||
|
import { useUserStore } from "@/stores/user";
|
||||||
|
import { exportDataAsXls } from "@/utils/export";
|
||||||
|
|
||||||
log.debug("DashboardPersonsPage created");
|
log.debug("DashboardPersonsPage created");
|
||||||
|
|
||||||
|
|
@ -28,6 +33,7 @@ type MenuItem = {
|
||||||
};
|
};
|
||||||
|
|
||||||
const { t } = useTranslation();
|
const { t } = useTranslation();
|
||||||
|
const userStore = useUserStore();
|
||||||
|
|
||||||
const { loading, dashboardPersons } = useDashboardPersonsDueDates(props.mode);
|
const { loading, dashboardPersons } = useDashboardPersonsDueDates(props.mode);
|
||||||
|
|
||||||
|
|
@ -227,6 +233,32 @@ function personRoleDisplayValue(personCourseSession: DashboardPersonCourseSessio
|
||||||
return "";
|
return "";
|
||||||
}
|
}
|
||||||
|
|
||||||
|
function exportData() {
|
||||||
|
const courseSessionIdsSet = new Set<string>();
|
||||||
|
// get all course session ids from users
|
||||||
|
if (selectedSession.value.id === UNFILTERED) {
|
||||||
|
for (const person of filteredPersons.value) {
|
||||||
|
for (const courseSession of person.course_sessions) {
|
||||||
|
courseSessionIdsSet.add(courseSession.id);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
courseSessionIdsSet.add(selectedSession.value.id);
|
||||||
|
}
|
||||||
|
|
||||||
|
// construct StatisticsFilterItems for export call
|
||||||
|
const items: StatisticsFilterItem[] = [];
|
||||||
|
for (const csId of courseSessionIdsSet) {
|
||||||
|
items.push({
|
||||||
|
_id: "",
|
||||||
|
course_session_id: csId,
|
||||||
|
generation: "",
|
||||||
|
circle_id: "",
|
||||||
|
});
|
||||||
|
}
|
||||||
|
exportDataAsXls(items, exportPersons, userStore.language);
|
||||||
|
}
|
||||||
|
|
||||||
watch(selectedCourse, () => {
|
watch(selectedCourse, () => {
|
||||||
selectedRegion.value = regions.value[0];
|
selectedRegion.value = regions.value[0];
|
||||||
});
|
});
|
||||||
|
|
@ -253,7 +285,18 @@ watch(selectedRegion, () => {
|
||||||
<it-icon-arrow-left class="-ml-1 mr-1 h-5 w-5"></it-icon-arrow-left>
|
<it-icon-arrow-left class="-ml-1 mr-1 h-5 w-5"></it-icon-arrow-left>
|
||||||
<span class="inline">{{ $t("general.back") }}</span>
|
<span class="inline">{{ $t("general.back") }}</span>
|
||||||
</router-link>
|
</router-link>
|
||||||
|
<div class="mb-10 flex items-center justify-between">
|
||||||
<h2 class="my-4">{{ $t("a.Personen") }}</h2>
|
<h2 class="my-4">{{ $t("a.Personen") }}</h2>
|
||||||
|
<button
|
||||||
|
v-if="userStore.course_session_experts.length > 0"
|
||||||
|
class="flex"
|
||||||
|
data-cy="export-button"
|
||||||
|
@click="exportData"
|
||||||
|
>
|
||||||
|
<it-icon-export></it-icon-export>
|
||||||
|
<span class="ml inline-block">{{ $t("a.Als Excel exportieren") }}</span>
|
||||||
|
</button>
|
||||||
|
</div>
|
||||||
<div class="bg-white px-4 py-2">
|
<div class="bg-white px-4 py-2">
|
||||||
<section
|
<section
|
||||||
v-if="filtersVisible"
|
v-if="filtersVisible"
|
||||||
|
|
|
||||||
|
|
@ -221,6 +221,15 @@ export async function exportCompetenceElements(
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
|
export async function exportPersons(
|
||||||
|
data: XlsExportRequestData,
|
||||||
|
language: string
|
||||||
|
): Promise<XlsExportResponseData> {
|
||||||
|
return await itPost("/api/dashboard/export/persons/", data, {
|
||||||
|
headers: { "Accept-Language": language },
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
export function courseIdForCourseSlug(
|
export function courseIdForCourseSlug(
|
||||||
dashboardConfigs: DashboardCourseConfigType[],
|
dashboardConfigs: DashboardCourseConfigType[],
|
||||||
courseSlug: string
|
courseSlug: string
|
||||||
|
|
|
||||||
|
|
@ -26,7 +26,7 @@ function getCurrentDate() {
|
||||||
function verifyExportFileExists(fileName) {
|
function verifyExportFileExists(fileName) {
|
||||||
const downloadsFolder = Cypress.config("downloadsFolder");
|
const downloadsFolder = Cypress.config("downloadsFolder");
|
||||||
cy.readFile(
|
cy.readFile(
|
||||||
path.join(downloadsFolder, `${fileName}_${getCurrentDate()}.xlsx`)
|
path.join(downloadsFolder, `${fileName}_${getCurrentDate()}.xlsx`),
|
||||||
).should("exist");
|
).should("exist");
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -39,7 +39,7 @@ function testExport(url, fileName) {
|
||||||
describe("dashboardExport.cy.js", () => {
|
describe("dashboardExport.cy.js", () => {
|
||||||
beforeEach(() => {
|
beforeEach(() => {
|
||||||
cy.manageCommand(
|
cy.manageCommand(
|
||||||
"cypress_reset --create-assignment-evaluation --create-feedback-responses --create-course-completion-performance-criteria --create-attendance-days"
|
"cypress_reset --create-assignment-evaluation --create-feedback-responses --create-course-completion-performance-criteria --create-attendance-days",
|
||||||
);
|
);
|
||||||
});
|
});
|
||||||
|
|
||||||
|
|
@ -55,13 +55,17 @@ describe("dashboardExport.cy.js", () => {
|
||||||
it("should download the competence elements export", () => {
|
it("should download the competence elements export", () => {
|
||||||
testExport(
|
testExport(
|
||||||
"/statistic/test-lehrgang/assignment",
|
"/statistic/test-lehrgang/assignment",
|
||||||
"export_kompetenznachweis_elemente"
|
"export_kompetenznachweis_elemente",
|
||||||
);
|
);
|
||||||
});
|
});
|
||||||
|
|
||||||
it("should download the feedback export", () => {
|
it("should download the feedback export", () => {
|
||||||
testExport("/statistic/test-lehrgang/feedback", "export_feedback");
|
testExport("/statistic/test-lehrgang/feedback", "export_feedback");
|
||||||
});
|
});
|
||||||
|
|
||||||
|
it("should download the person export", () => {
|
||||||
|
testExport("/dashboard/persons", "export_personen");
|
||||||
|
});
|
||||||
});
|
});
|
||||||
|
|
||||||
describe("as trainer", () => {
|
describe("as trainer", () => {
|
||||||
|
|
@ -76,12 +80,16 @@ describe("dashboardExport.cy.js", () => {
|
||||||
it("should download the competence elements export", () => {
|
it("should download the competence elements export", () => {
|
||||||
testExport(
|
testExport(
|
||||||
"/statistic/test-lehrgang/assignment",
|
"/statistic/test-lehrgang/assignment",
|
||||||
"export_kompetenznachweis_elemente"
|
"export_kompetenznachweis_elemente",
|
||||||
);
|
);
|
||||||
});
|
});
|
||||||
|
|
||||||
it("should download the feedback export", () => {
|
it("should download the feedback export", () => {
|
||||||
testExport("/statistic/test-lehrgang/feedback", "export_feedback");
|
testExport("/statistic/test-lehrgang/feedback", "export_feedback");
|
||||||
});
|
});
|
||||||
|
|
||||||
|
it("should download the person export", () => {
|
||||||
|
testExport("/dashboard/persons", "export_personen");
|
||||||
|
});
|
||||||
});
|
});
|
||||||
});
|
});
|
||||||
|
|
|
||||||
|
|
@ -44,6 +44,7 @@ from vbv_lernwelt.dashboard.views import (
|
||||||
export_attendance_as_xsl,
|
export_attendance_as_xsl,
|
||||||
export_competence_elements_as_xsl,
|
export_competence_elements_as_xsl,
|
||||||
export_feedback_as_xsl,
|
export_feedback_as_xsl,
|
||||||
|
export_persons_as_xsl,
|
||||||
get_dashboard_config,
|
get_dashboard_config,
|
||||||
get_dashboard_due_dates,
|
get_dashboard_due_dates,
|
||||||
get_dashboard_persons,
|
get_dashboard_persons,
|
||||||
|
|
@ -143,6 +144,7 @@ urlpatterns = [
|
||||||
path(r"api/dashboard/export/competence_elements/", export_competence_elements_as_xsl,
|
path(r"api/dashboard/export/competence_elements/", export_competence_elements_as_xsl,
|
||||||
name="export_certificate_as_xsl"),
|
name="export_certificate_as_xsl"),
|
||||||
path(r"api/dashboard/export/feedback/", export_feedback_as_xsl, name="export_feedback_as_xsl"),
|
path(r"api/dashboard/export/feedback/", export_feedback_as_xsl, name="export_feedback_as_xsl"),
|
||||||
|
path(r"api/dashboard/export/persons/", export_persons_as_xsl, name="export_persons_as_xsl"),
|
||||||
|
|
||||||
# course
|
# course
|
||||||
path(r"api/course/sessions/", get_course_sessions, name="get_course_sessions"),
|
path(r"api/course/sessions/", get_course_sessions, name="get_course_sessions"),
|
||||||
|
|
|
||||||
|
|
@ -87,7 +87,7 @@ msgstr ""
|
||||||
msgid "Lehrgang-Seite"
|
msgid "Lehrgang-Seite"
|
||||||
msgstr ""
|
msgstr ""
|
||||||
|
|
||||||
#: vbv_lernwelt/course/models.py:278
|
#: vbv_lernwelt/dashboard/person_export.py:116
|
||||||
msgid "Teilnehmer"
|
msgid "Teilnehmer"
|
||||||
msgstr ""
|
msgstr ""
|
||||||
|
|
||||||
|
|
@ -111,7 +111,7 @@ msgstr ""
|
||||||
msgid "Versicherungsvermittler-Lehrgang"
|
msgid "Versicherungsvermittler-Lehrgang"
|
||||||
msgstr ""
|
msgstr ""
|
||||||
|
|
||||||
#: vbv_lernwelt/course/models.py:351
|
#: vbv_lernwelt/course/models.py:350
|
||||||
msgid "ÜK-Lehrgang"
|
msgid "ÜK-Lehrgang"
|
||||||
msgstr ""
|
msgstr ""
|
||||||
|
|
||||||
|
|
@ -155,10 +155,30 @@ msgstr ""
|
||||||
msgid "Email"
|
msgid "Email"
|
||||||
msgstr "Email"
|
msgstr "Email"
|
||||||
|
|
||||||
#: vbv_lernwelt/course_session/services/export_attendance.py:138
|
#: vbv_lernwelt/course_session/services/export_attendance.py:127
|
||||||
msgid "Lehrvertragsnummer"
|
msgid "Lehrvertragsnummer"
|
||||||
msgstr ""
|
msgstr ""
|
||||||
|
|
||||||
|
#: vbv_lernwelt/dashboard/person_export.py:16
|
||||||
|
msgid "export_personen"
|
||||||
|
msgstr ""
|
||||||
|
|
||||||
|
#: vbv_lernwelt/dashboard/person_export.py:68
|
||||||
|
msgid "Telefon"
|
||||||
|
msgstr ""
|
||||||
|
|
||||||
|
#: vbv_lernwelt/dashboard/person_export.py:69
|
||||||
|
msgid "Rolle"
|
||||||
|
msgstr ""
|
||||||
|
|
||||||
|
#: vbv_lernwelt/dashboard/person_export.py:118
|
||||||
|
msgid "Trainer"
|
||||||
|
msgstr ""
|
||||||
|
|
||||||
|
#: vbv_lernwelt/dashboard/person_export.py:120
|
||||||
|
msgid "Regionenleiter"
|
||||||
|
msgstr ""
|
||||||
|
|
||||||
#: vbv_lernwelt/feedback/export.py:19
|
#: vbv_lernwelt/feedback/export.py:19
|
||||||
msgid "export_feedback"
|
msgid "export_feedback"
|
||||||
msgstr ""
|
msgstr ""
|
||||||
|
|
|
||||||
Binary file not shown.
|
|
@ -8,7 +8,7 @@ msgid ""
|
||||||
msgstr ""
|
msgstr ""
|
||||||
"Project-Id-Version: PACKAGE VERSION\n"
|
"Project-Id-Version: PACKAGE VERSION\n"
|
||||||
"Report-Msgid-Bugs-To: \n"
|
"Report-Msgid-Bugs-To: \n"
|
||||||
"POT-Creation-Date: 2024-07-27 20:59+0200\n"
|
"POT-Creation-Date: 2024-07-30 11:16+0200\n"
|
||||||
"PO-Revision-Date: YEAR-MO-DA HO:MI+ZONE\n"
|
"PO-Revision-Date: YEAR-MO-DA HO:MI+ZONE\n"
|
||||||
"Last-Translator: FULL NAME <EMAIL@ADDRESS>\n"
|
"Last-Translator: FULL NAME <EMAIL@ADDRESS>\n"
|
||||||
"Language-Team: LANGUAGE <LL@li.org>\n"
|
"Language-Team: LANGUAGE <LL@li.org>\n"
|
||||||
|
|
@ -88,8 +88,9 @@ msgid "Lehrgang-Seite"
|
||||||
msgstr ""
|
msgstr ""
|
||||||
|
|
||||||
#: vbv_lernwelt/course/models.py:278
|
#: vbv_lernwelt/course/models.py:278
|
||||||
|
#: vbv_lernwelt/dashboard/person_export.py:116
|
||||||
msgid "Teilnehmer"
|
msgid "Teilnehmer"
|
||||||
msgstr ""
|
msgstr "Participant"
|
||||||
|
|
||||||
#: vbv_lernwelt/course/models.py:279
|
#: vbv_lernwelt/course/models.py:279
|
||||||
msgid "Experte/Trainer"
|
msgid "Experte/Trainer"
|
||||||
|
|
@ -120,7 +121,6 @@ msgid "export_anwesenheit"
|
||||||
msgstr "export_presence"
|
msgstr "export_presence"
|
||||||
|
|
||||||
#: vbv_lernwelt/course_session/services/export_attendance.py:86
|
#: vbv_lernwelt/course_session/services/export_attendance.py:86
|
||||||
#| msgid "Anwesenheit"
|
|
||||||
msgid "Optionale Anwesenheit"
|
msgid "Optionale Anwesenheit"
|
||||||
msgstr "Présence facultative"
|
msgstr "Présence facultative"
|
||||||
|
|
||||||
|
|
@ -160,6 +160,26 @@ msgstr "E-mail"
|
||||||
msgid "Lehrvertragsnummer"
|
msgid "Lehrvertragsnummer"
|
||||||
msgstr "Numéro de contrat d'apprentissage"
|
msgstr "Numéro de contrat d'apprentissage"
|
||||||
|
|
||||||
|
#: vbv_lernwelt/dashboard/person_export.py:16
|
||||||
|
msgid "export_personen"
|
||||||
|
msgstr "export_personnes"
|
||||||
|
|
||||||
|
#: vbv_lernwelt/dashboard/person_export.py:68
|
||||||
|
msgid "Telefon"
|
||||||
|
msgstr "Téléphone"
|
||||||
|
|
||||||
|
#: vbv_lernwelt/dashboard/person_export.py:69
|
||||||
|
msgid "Rolle"
|
||||||
|
msgstr "Rôle"
|
||||||
|
|
||||||
|
#: vbv_lernwelt/dashboard/person_export.py:118
|
||||||
|
msgid "Trainer"
|
||||||
|
msgstr "Formateur / Formatrice"
|
||||||
|
|
||||||
|
#: vbv_lernwelt/dashboard/person_export.py:120
|
||||||
|
msgid "Regionenleiter"
|
||||||
|
msgstr "Responsable CI"
|
||||||
|
|
||||||
#: vbv_lernwelt/feedback/export.py:19
|
#: vbv_lernwelt/feedback/export.py:19
|
||||||
msgid "export_feedback"
|
msgid "export_feedback"
|
||||||
msgstr "export_feedback"
|
msgstr "export_feedback"
|
||||||
|
|
|
||||||
Binary file not shown.
|
|
@ -8,7 +8,7 @@ msgid ""
|
||||||
msgstr ""
|
msgstr ""
|
||||||
"Project-Id-Version: PACKAGE VERSION\n"
|
"Project-Id-Version: PACKAGE VERSION\n"
|
||||||
"Report-Msgid-Bugs-To: \n"
|
"Report-Msgid-Bugs-To: \n"
|
||||||
"POT-Creation-Date: 2024-07-27 20:59+0200\n"
|
"POT-Creation-Date: 2024-07-30 11:16+0200\n"
|
||||||
"PO-Revision-Date: YEAR-MO-DA HO:MI+ZONE\n"
|
"PO-Revision-Date: YEAR-MO-DA HO:MI+ZONE\n"
|
||||||
"Last-Translator: FULL NAME <EMAIL@ADDRESS>\n"
|
"Last-Translator: FULL NAME <EMAIL@ADDRESS>\n"
|
||||||
"Language-Team: LANGUAGE <LL@li.org>\n"
|
"Language-Team: LANGUAGE <LL@li.org>\n"
|
||||||
|
|
@ -88,8 +88,9 @@ msgid "Lehrgang-Seite"
|
||||||
msgstr ""
|
msgstr ""
|
||||||
|
|
||||||
#: vbv_lernwelt/course/models.py:278
|
#: vbv_lernwelt/course/models.py:278
|
||||||
|
#: vbv_lernwelt/dashboard/person_export.py:116
|
||||||
msgid "Teilnehmer"
|
msgid "Teilnehmer"
|
||||||
msgstr ""
|
msgstr "Partecipante"
|
||||||
|
|
||||||
#: vbv_lernwelt/course/models.py:279
|
#: vbv_lernwelt/course/models.py:279
|
||||||
msgid "Experte/Trainer"
|
msgid "Experte/Trainer"
|
||||||
|
|
@ -120,7 +121,6 @@ msgid "export_anwesenheit"
|
||||||
msgstr "esportazione_presenza"
|
msgstr "esportazione_presenza"
|
||||||
|
|
||||||
#: vbv_lernwelt/course_session/services/export_attendance.py:86
|
#: vbv_lernwelt/course_session/services/export_attendance.py:86
|
||||||
#| msgid "Anwesenheit"
|
|
||||||
msgid "Optionale Anwesenheit"
|
msgid "Optionale Anwesenheit"
|
||||||
msgstr "Presenza opzionale"
|
msgstr "Presenza opzionale"
|
||||||
|
|
||||||
|
|
@ -160,6 +160,28 @@ msgstr "Email"
|
||||||
msgid "Lehrvertragsnummer"
|
msgid "Lehrvertragsnummer"
|
||||||
msgstr "Numero di contratto di tirocinio"
|
msgstr "Numero di contratto di tirocinio"
|
||||||
|
|
||||||
|
#: vbv_lernwelt/dashboard/person_export.py:16
|
||||||
|
#, fuzzy
|
||||||
|
#| msgid "export_anwesenheit"
|
||||||
|
msgid "export_personen"
|
||||||
|
msgstr "esportazione_persone"
|
||||||
|
|
||||||
|
#: vbv_lernwelt/dashboard/person_export.py:68
|
||||||
|
msgid "Telefon"
|
||||||
|
msgstr "Telefono"
|
||||||
|
|
||||||
|
#: vbv_lernwelt/dashboard/person_export.py:69
|
||||||
|
msgid "Rolle"
|
||||||
|
msgstr "Ruolo"
|
||||||
|
|
||||||
|
#: vbv_lernwelt/dashboard/person_export.py:118
|
||||||
|
msgid "Trainer"
|
||||||
|
msgstr "Trainer"
|
||||||
|
|
||||||
|
#: vbv_lernwelt/dashboard/person_export.py:120
|
||||||
|
msgid "Regionenleiter"
|
||||||
|
msgstr "Responsabile CI"
|
||||||
|
|
||||||
#: vbv_lernwelt/feedback/export.py:19
|
#: vbv_lernwelt/feedback/export.py:19
|
||||||
msgid "export_feedback"
|
msgid "export_feedback"
|
||||||
msgstr "esportazione_feedback"
|
msgstr "esportazione_feedback"
|
||||||
|
|
|
||||||
|
|
@ -0,0 +1,123 @@
|
||||||
|
from io import BytesIO
|
||||||
|
from typing import Optional
|
||||||
|
|
||||||
|
import structlog
|
||||||
|
from django.utils.translation import gettext_lazy as _
|
||||||
|
from openpyxl import Workbook
|
||||||
|
|
||||||
|
from vbv_lernwelt.core.models import User
|
||||||
|
from vbv_lernwelt.course.models import CourseSession
|
||||||
|
from vbv_lernwelt.course_session.services.export_attendance import (
|
||||||
|
add_user_headers,
|
||||||
|
make_export_filename,
|
||||||
|
sanitize_sheet_name,
|
||||||
|
)
|
||||||
|
from vbv_lernwelt.dashboard.utils import create_person_list_with_roles
|
||||||
|
|
||||||
|
PERSONS_EXPORT_FILENAME = _("export_personen")
|
||||||
|
|
||||||
|
logger = structlog.get_logger(__name__)
|
||||||
|
|
||||||
|
|
||||||
|
def export_persons(
|
||||||
|
user: User,
|
||||||
|
course_session_ids: list[str],
|
||||||
|
save_as_file: bool = False,
|
||||||
|
) -> Optional[bytes]:
|
||||||
|
if not course_session_ids:
|
||||||
|
return
|
||||||
|
|
||||||
|
wb = Workbook()
|
||||||
|
# remove the first sheet is just easier than keeping track of the active sheet
|
||||||
|
wb.remove(wb.active)
|
||||||
|
|
||||||
|
user_with_roles = create_person_list_with_roles(user, course_session_ids)
|
||||||
|
course_sessions = CourseSession.objects.filter(id__in=course_session_ids)
|
||||||
|
|
||||||
|
for cs in course_sessions:
|
||||||
|
_create_sheet(
|
||||||
|
wb,
|
||||||
|
cs.title,
|
||||||
|
cs.id,
|
||||||
|
user_with_roles,
|
||||||
|
)
|
||||||
|
|
||||||
|
if save_as_file:
|
||||||
|
wb.save(make_export_filename(PERSONS_EXPORT_FILENAME))
|
||||||
|
else:
|
||||||
|
output = BytesIO()
|
||||||
|
wb.save(output)
|
||||||
|
|
||||||
|
output.seek(0)
|
||||||
|
return output.getvalue()
|
||||||
|
|
||||||
|
|
||||||
|
def _create_sheet(
|
||||||
|
wb: Workbook,
|
||||||
|
title: str,
|
||||||
|
cs_id: int,
|
||||||
|
user_with_roles,
|
||||||
|
):
|
||||||
|
sheet = wb.create_sheet(title=sanitize_sheet_name(title))
|
||||||
|
|
||||||
|
if len(user_with_roles) == 0:
|
||||||
|
return sheet
|
||||||
|
|
||||||
|
# headers
|
||||||
|
# common user headers, Circle <title> <learningcontenttitle> bestanden, Circle <title> <learningcontenttitle> Resultat, ...
|
||||||
|
col_idx = add_user_headers(sheet)
|
||||||
|
sheet.cell(row=1, column=col_idx, value=str(_("Telefon")))
|
||||||
|
sheet.cell(row=1, column=col_idx + 1, value=str(_("Rolle")))
|
||||||
|
|
||||||
|
_add_rows(sheet, user_with_roles, cs_id)
|
||||||
|
|
||||||
|
return sheet
|
||||||
|
|
||||||
|
|
||||||
|
def _add_rows(
|
||||||
|
sheet,
|
||||||
|
users,
|
||||||
|
course_session_id,
|
||||||
|
):
|
||||||
|
idx_offset = 0
|
||||||
|
|
||||||
|
for row_idx, user in enumerate(users, start=2):
|
||||||
|
|
||||||
|
def get_user_cs_by_id(user_cs, cs_id):
|
||||||
|
return next((cs for cs in user_cs if int(cs.get("id")) == cs_id), None)
|
||||||
|
|
||||||
|
user_cs = get_user_cs_by_id(user["course_sessions"], course_session_id)
|
||||||
|
|
||||||
|
if not user_cs:
|
||||||
|
logger.warning(
|
||||||
|
"User not found in course session",
|
||||||
|
user_id=user["user_id"],
|
||||||
|
course_session_id=course_session_id,
|
||||||
|
)
|
||||||
|
idx_offset += 1
|
||||||
|
continue
|
||||||
|
|
||||||
|
user_role = _role_as_string(user_cs.get("user_role"))
|
||||||
|
idx = row_idx - idx_offset
|
||||||
|
|
||||||
|
sheet.cell(row=idx, column=1, value=user["first_name"])
|
||||||
|
sheet.cell(row=idx, column=2, value=user["last_name"])
|
||||||
|
sheet.cell(row=idx, column=3, value=user["email"])
|
||||||
|
sheet.cell(row=idx, column=4, value=user.get("Lehrvertragsnummer", ""))
|
||||||
|
sheet.cell(
|
||||||
|
row=idx,
|
||||||
|
column=5,
|
||||||
|
value=user.get("phone_number", ""),
|
||||||
|
)
|
||||||
|
sheet.cell(row=idx, column=6, value=user_role)
|
||||||
|
|
||||||
|
|
||||||
|
def _role_as_string(role):
|
||||||
|
if role == "MEMBER":
|
||||||
|
return str(_("Teilnehmer"))
|
||||||
|
elif role == "EXPERT":
|
||||||
|
return str(_("Trainer"))
|
||||||
|
elif role == "SUPERVISOR":
|
||||||
|
return str(_("Regionenleiter"))
|
||||||
|
else:
|
||||||
|
return role
|
||||||
|
|
@ -0,0 +1,186 @@
|
||||||
|
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,
|
||||||
|
TEST_STUDENT3_USER_ID,
|
||||||
|
TEST_TRAINER1_USER_ID,
|
||||||
|
TEST_TRAINER2_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.tests.test_attendance_export import ExportBaseTestCase
|
||||||
|
from vbv_lernwelt.dashboard.person_export import export_persons
|
||||||
|
from vbv_lernwelt.learnpath.models import Circle
|
||||||
|
|
||||||
|
|
||||||
|
class PersonsExportTestCase(ExportBaseTestCase):
|
||||||
|
def setUp(self):
|
||||||
|
super().setUp()
|
||||||
|
|
||||||
|
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.circle_fahrzeug = Circle.objects.get(
|
||||||
|
slug="test-lehrgang-lp-circle-fahrzeug"
|
||||||
|
)
|
||||||
|
self.circle_reisen = Circle.objects.get(slug="test-lehrgang-lp-circle-fahrzeug")
|
||||||
|
|
||||||
|
self.test_trainer1 = User.objects.get(id=TEST_TRAINER1_USER_ID)
|
||||||
|
self.test_trainer2 = User.objects.get(id=TEST_TRAINER2_USER_ID)
|
||||||
|
self.test_student1 = User.objects.get(id=TEST_STUDENT1_USER_ID)
|
||||||
|
self.test_student2 = User.objects.get(id=TEST_STUDENT2_USER_ID)
|
||||||
|
self.test_student3 = User.objects.get(id=TEST_STUDENT3_USER_ID)
|
||||||
|
|
||||||
|
self.test_student1_row = [
|
||||||
|
self.test_student1.first_name,
|
||||||
|
self.test_student1.last_name,
|
||||||
|
self.test_student1.email,
|
||||||
|
None,
|
||||||
|
None,
|
||||||
|
"Teilnehmer",
|
||||||
|
]
|
||||||
|
self.test_student2_row = [
|
||||||
|
self.test_student2.first_name,
|
||||||
|
self.test_student2.last_name,
|
||||||
|
self.test_student2.email,
|
||||||
|
None,
|
||||||
|
None,
|
||||||
|
"Teilnehmer",
|
||||||
|
]
|
||||||
|
self.test_student3_row = [
|
||||||
|
self.test_student3.first_name,
|
||||||
|
self.test_student3.last_name,
|
||||||
|
self.test_student3.email,
|
||||||
|
None,
|
||||||
|
None,
|
||||||
|
"Teilnehmer",
|
||||||
|
]
|
||||||
|
self.test_trainer1_row = [
|
||||||
|
self.test_trainer1.first_name,
|
||||||
|
self.test_trainer1.last_name,
|
||||||
|
self.test_trainer1.email,
|
||||||
|
None,
|
||||||
|
None,
|
||||||
|
"Trainer",
|
||||||
|
]
|
||||||
|
self.test_trainer2_row = [
|
||||||
|
self.test_trainer2.first_name,
|
||||||
|
self.test_trainer2.last_name,
|
||||||
|
self.test_trainer2.email,
|
||||||
|
None,
|
||||||
|
None,
|
||||||
|
"Trainer",
|
||||||
|
]
|
||||||
|
|
||||||
|
def _generate_expected_data(self, rows):
|
||||||
|
expected_data = [
|
||||||
|
self._make_header(),
|
||||||
|
]
|
||||||
|
for r in rows:
|
||||||
|
expected_data.append(r)
|
||||||
|
|
||||||
|
return expected_data
|
||||||
|
|
||||||
|
def _generate_workbook(self, user, course_session_ids):
|
||||||
|
export_data = io.BytesIO(
|
||||||
|
export_persons(user, course_session_ids, save_as_file=False)
|
||||||
|
)
|
||||||
|
return load_workbook(export_data)
|
||||||
|
|
||||||
|
def _make_header(self):
|
||||||
|
return [
|
||||||
|
"Vorname",
|
||||||
|
"Nachname",
|
||||||
|
"Email",
|
||||||
|
"Lehrvertragsnummer",
|
||||||
|
"Telefon",
|
||||||
|
"Rolle",
|
||||||
|
]
|
||||||
|
|
||||||
|
def test_export_persons(self):
|
||||||
|
wb = self._generate_workbook(self.test_trainer1, [self.course_session_be.id])
|
||||||
|
self.assertEqual(len(wb.sheetnames), 1)
|
||||||
|
self.assertEqual(wb.sheetnames[0], "Test Bern 2022 a")
|
||||||
|
wb.active = wb["Test Bern 2022 a"]
|
||||||
|
|
||||||
|
data = self._generate_expected_data(
|
||||||
|
[
|
||||||
|
self.test_student1_row,
|
||||||
|
self.test_student2_row,
|
||||||
|
self.test_student3_row,
|
||||||
|
self.test_trainer1_row,
|
||||||
|
]
|
||||||
|
)
|
||||||
|
|
||||||
|
self._check_export(wb, data, 4, 6)
|
||||||
|
|
||||||
|
wb = self._generate_workbook(self.test_trainer2, [self.course_session_zh.id])
|
||||||
|
self.assertEqual(len(wb.sheetnames), 1)
|
||||||
|
self.assertEqual(wb.sheetnames[0], "Test Zürich 2022 a")
|
||||||
|
wb.active = wb["Test Zürich 2022 a"]
|
||||||
|
|
||||||
|
data = self._generate_expected_data(
|
||||||
|
[self.test_student2_row, self.test_trainer2_row]
|
||||||
|
)
|
||||||
|
self._check_export(wb, data, 3, 6)
|
||||||
|
|
||||||
|
def test_cannot_export_other_session(self):
|
||||||
|
wb = self._generate_workbook(self.test_trainer1, [self.course_session_zh.id])
|
||||||
|
self.assertEqual(len(wb.sheetnames), 1)
|
||||||
|
self.assertEqual(wb.sheetnames[0], "Test Zürich 2022 a")
|
||||||
|
wb.active = wb["Test Zürich 2022 a"]
|
||||||
|
|
||||||
|
data = self._generate_expected_data([[None] * 6])
|
||||||
|
|
||||||
|
self._check_export(wb, data, 1, 6)
|
||||||
|
|
||||||
|
def test_export_in_fr(self):
|
||||||
|
activate("fr")
|
||||||
|
wb = self._generate_workbook(self.test_trainer1, [self.course_session_be.id])
|
||||||
|
self.assertEqual(len(wb.sheetnames), 1)
|
||||||
|
self.assertEqual(wb.sheetnames[0], "Test Bern 2022 a")
|
||||||
|
wb.active = wb["Test Bern 2022 a"]
|
||||||
|
|
||||||
|
header = [
|
||||||
|
"Prénom",
|
||||||
|
"Nom de famille",
|
||||||
|
"E-mail",
|
||||||
|
"Numéro de contrat d'apprentissage",
|
||||||
|
"Téléphone",
|
||||||
|
"Rôle",
|
||||||
|
]
|
||||||
|
|
||||||
|
self.assertEqual([cell.value for cell in wb.active[1]], header)
|
||||||
|
self.assertEqual(wb.active.cell(row=2, column=6).value, "Participant")
|
||||||
|
self.assertEqual(
|
||||||
|
wb.active.cell(row=5, column=6).value, "Formateur / Formatrice"
|
||||||
|
)
|
||||||
|
|
||||||
|
def test_export_in_it(self):
|
||||||
|
activate("it")
|
||||||
|
wb = self._generate_workbook(self.test_trainer1, [self.course_session_be.id])
|
||||||
|
self.assertEqual(len(wb.sheetnames), 1)
|
||||||
|
self.assertEqual(wb.sheetnames[0], "Test Bern 2022 a")
|
||||||
|
wb.active = wb["Test Bern 2022 a"]
|
||||||
|
|
||||||
|
header = [
|
||||||
|
"Nome",
|
||||||
|
"Cognome",
|
||||||
|
"Email",
|
||||||
|
"Numero di contratto di tirocinio",
|
||||||
|
"Telefono",
|
||||||
|
"Ruolo",
|
||||||
|
]
|
||||||
|
|
||||||
|
self.assertEqual([cell.value for cell in wb.active[1]], header)
|
||||||
|
self.assertEqual(wb.active.cell(row=2, column=6).value, "Partecipante")
|
||||||
|
self.assertEqual(wb.active.cell(row=5, column=6).value, "Trainer")
|
||||||
|
|
@ -0,0 +1,179 @@
|
||||||
|
from dataclasses import dataclass
|
||||||
|
from typing import List, Set
|
||||||
|
|
||||||
|
from vbv_lernwelt.core.models import User
|
||||||
|
from vbv_lernwelt.course.models import CourseSession, CourseSessionUser
|
||||||
|
from vbv_lernwelt.course_session_group.models import CourseSessionGroup
|
||||||
|
from vbv_lernwelt.learning_mentor.models import LearningMentor
|
||||||
|
|
||||||
|
|
||||||
|
@dataclass(frozen=True)
|
||||||
|
class CourseSessionWithRoles:
|
||||||
|
_original: CourseSession
|
||||||
|
roles: Set[str]
|
||||||
|
|
||||||
|
def __getattr__(self, name: str):
|
||||||
|
# Delegate attribute access to the _original CourseSession object
|
||||||
|
return getattr(self._original, name)
|
||||||
|
|
||||||
|
def save(self, *args, **kwargs):
|
||||||
|
raise NotImplementedError("This proxy object cannot be saved.")
|
||||||
|
|
||||||
|
|
||||||
|
def get_course_sessions_with_roles_for_user(user: User) -> List[CourseSessionWithRoles]:
|
||||||
|
result_course_sessions = {}
|
||||||
|
|
||||||
|
# participant/member/expert course sessions
|
||||||
|
csu_qs = CourseSessionUser.objects.filter(user=user).prefetch_related(
|
||||||
|
"course_session", "course_session__course"
|
||||||
|
)
|
||||||
|
for csu in csu_qs:
|
||||||
|
cs = csu.course_session
|
||||||
|
# member/expert is mutually exclusive...
|
||||||
|
cs.roles = {csu.role}
|
||||||
|
result_course_sessions[cs.id] = cs
|
||||||
|
|
||||||
|
# enrich with supervisor course sessions
|
||||||
|
csg_qs = CourseSessionGroup.objects.filter(supervisor=user).prefetch_related(
|
||||||
|
"course_session", "course_session__course"
|
||||||
|
)
|
||||||
|
for csg in csg_qs:
|
||||||
|
for cs in csg.course_session.all():
|
||||||
|
cs.roles = set()
|
||||||
|
cs = result_course_sessions.get(cs.id, cs)
|
||||||
|
|
||||||
|
cs.roles.add("SUPERVISOR")
|
||||||
|
result_course_sessions[cs.id] = cs
|
||||||
|
|
||||||
|
# enrich with mentor course sessions
|
||||||
|
lm_qs = LearningMentor.objects.filter(mentor=user).prefetch_related(
|
||||||
|
"course_session", "course_session__course"
|
||||||
|
)
|
||||||
|
for lm in lm_qs:
|
||||||
|
cs = lm.course_session
|
||||||
|
cs.roles = set()
|
||||||
|
cs = result_course_sessions.get(cs.id, cs)
|
||||||
|
|
||||||
|
cs.roles.add("LEARNING_MENTOR")
|
||||||
|
result_course_sessions[cs.id] = cs
|
||||||
|
|
||||||
|
return [
|
||||||
|
CourseSessionWithRoles(cs, cs.roles) for cs in result_course_sessions.values()
|
||||||
|
]
|
||||||
|
|
||||||
|
|
||||||
|
def has_cs_role(roles: Set[str]) -> bool:
|
||||||
|
return bool(roles & {"SUPERVISOR", "EXPERT", "MEMBER"})
|
||||||
|
|
||||||
|
|
||||||
|
def user_role(roles: Set[str]) -> str:
|
||||||
|
if "SUPERVISOR" in roles:
|
||||||
|
return "SUPERVISOR"
|
||||||
|
if "EXPERT" in roles:
|
||||||
|
return "EXPERT"
|
||||||
|
if "MEMBER" in roles:
|
||||||
|
return "MEMBER"
|
||||||
|
return "LEARNING_MENTOR"
|
||||||
|
|
||||||
|
|
||||||
|
def create_course_session_dict(course_session_object, my_role, user_role):
|
||||||
|
return {
|
||||||
|
"id": str(course_session_object.id),
|
||||||
|
"session_title": course_session_object.title,
|
||||||
|
"course_id": str(course_session_object.course.id),
|
||||||
|
"course_title": course_session_object.course.title,
|
||||||
|
"course_slug": course_session_object.course.slug,
|
||||||
|
"region": course_session_object.region,
|
||||||
|
"generation": course_session_object.generation,
|
||||||
|
"my_role": my_role,
|
||||||
|
"user_role": user_role,
|
||||||
|
"is_uk": course_session_object.course.configuration.is_uk,
|
||||||
|
"is_vv": course_session_object.course.configuration.is_vv,
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
def create_person_list_with_roles(
|
||||||
|
user, course_session_ids=None, include_private_data=False
|
||||||
|
):
|
||||||
|
def create_user_dict(user_object):
|
||||||
|
user_data = {
|
||||||
|
"user_id": user_object.id,
|
||||||
|
"first_name": user_object.first_name,
|
||||||
|
"last_name": user_object.last_name,
|
||||||
|
"email": user_object.email,
|
||||||
|
"avatar_url_small": user_object.avatar_url_small,
|
||||||
|
"avatar_url": user_object.avatar_url,
|
||||||
|
"course_sessions": [],
|
||||||
|
}
|
||||||
|
if include_private_data:
|
||||||
|
user_data["phone_number"] = user_object.phone_number
|
||||||
|
user_data["Lehrvertragsnummer"] = user_object.additional_json_data.get(
|
||||||
|
"Lehrvertragsnummer", ""
|
||||||
|
)
|
||||||
|
|
||||||
|
return user_data
|
||||||
|
|
||||||
|
course_sessions = get_course_sessions_with_roles_for_user(user)
|
||||||
|
|
||||||
|
result_persons = {}
|
||||||
|
for cs in course_sessions:
|
||||||
|
if has_cs_role(cs.roles) and cs.course.configuration.is_uk:
|
||||||
|
course_session_users = CourseSessionUser.objects.filter(
|
||||||
|
course_session=cs.id
|
||||||
|
).select_related("user")
|
||||||
|
my_role = user_role(cs.roles)
|
||||||
|
for csu in course_session_users:
|
||||||
|
person_data = result_persons.get(
|
||||||
|
csu.user.id, create_user_dict(csu.user)
|
||||||
|
)
|
||||||
|
person_data["course_sessions"].append(
|
||||||
|
create_course_session_dict(cs, my_role, csu.role)
|
||||||
|
)
|
||||||
|
result_persons[csu.user.id] = person_data
|
||||||
|
|
||||||
|
# add persons where request.user is mentor
|
||||||
|
for cs in course_sessions:
|
||||||
|
if "LEARNING_MENTOR" in cs.roles:
|
||||||
|
lm = LearningMentor.objects.filter(
|
||||||
|
mentor=user, course_session=cs.id
|
||||||
|
).first()
|
||||||
|
|
||||||
|
for participant in lm.participants.all():
|
||||||
|
course_session_entry = create_course_session_dict(
|
||||||
|
cs,
|
||||||
|
"LEARNING_MENTOR",
|
||||||
|
"LEARNING_MENTEE",
|
||||||
|
)
|
||||||
|
|
||||||
|
if participant.user.id not in result_persons:
|
||||||
|
person_data = create_user_dict(participant.user)
|
||||||
|
person_data["course_sessions"] = [course_session_entry]
|
||||||
|
result_persons[participant.user.id] = person_data
|
||||||
|
else:
|
||||||
|
# user is already in result_persons
|
||||||
|
result_persons[participant.user.id]["course_sessions"].append(
|
||||||
|
course_session_entry
|
||||||
|
)
|
||||||
|
|
||||||
|
# add persons where request.user is mentee
|
||||||
|
mentor_relation_qs = LearningMentor.objects.filter(
|
||||||
|
participants__user=user
|
||||||
|
).prefetch_related("mentor", "course_session")
|
||||||
|
for mentor_relation in mentor_relation_qs:
|
||||||
|
cs = mentor_relation.course_session
|
||||||
|
course_session_entry = create_course_session_dict(
|
||||||
|
cs,
|
||||||
|
"LEARNING_MENTEE",
|
||||||
|
"LEARNING_MENTOR",
|
||||||
|
)
|
||||||
|
|
||||||
|
if mentor_relation.mentor.id not in result_persons:
|
||||||
|
person_data = create_user_dict(mentor_relation.mentor)
|
||||||
|
person_data["course_sessions"] = [course_session_entry]
|
||||||
|
result_persons[mentor_relation.mentor.id] = person_data
|
||||||
|
else:
|
||||||
|
# user is already in result_persons
|
||||||
|
result_persons[mentor_relation.mentor.id]["course_sessions"].append(
|
||||||
|
course_session_entry
|
||||||
|
)
|
||||||
|
return result_persons.values()
|
||||||
|
|
@ -2,7 +2,7 @@ import base64
|
||||||
from dataclasses import asdict, dataclass
|
from dataclasses import asdict, dataclass
|
||||||
from datetime import date
|
from datetime import date
|
||||||
from enum import Enum
|
from enum import Enum
|
||||||
from typing import List, Set, Tuple
|
from typing import List, Tuple
|
||||||
|
|
||||||
from django.db.models import Q
|
from django.db.models import Q
|
||||||
from django.http import HttpResponse
|
from django.http import HttpResponse
|
||||||
|
|
@ -24,25 +24,27 @@ from vbv_lernwelt.competence.services import (
|
||||||
query_competence_course_session_edoniq_tests,
|
query_competence_course_session_edoniq_tests,
|
||||||
)
|
)
|
||||||
from vbv_lernwelt.core.models import User
|
from vbv_lernwelt.core.models import User
|
||||||
from vbv_lernwelt.course.models import (
|
from vbv_lernwelt.course.models import CourseConfiguration, CourseSessionUser
|
||||||
CourseConfiguration,
|
|
||||||
CourseSession,
|
|
||||||
CourseSessionUser,
|
|
||||||
)
|
|
||||||
from vbv_lernwelt.course.views import logger
|
from vbv_lernwelt.course.views import logger
|
||||||
from vbv_lernwelt.course_session.services.export_attendance import (
|
from vbv_lernwelt.course_session.services.export_attendance import (
|
||||||
ATTENDANCE_EXPORT_FILENAME,
|
ATTENDANCE_EXPORT_FILENAME,
|
||||||
export_attendance,
|
export_attendance,
|
||||||
make_export_filename,
|
make_export_filename,
|
||||||
)
|
)
|
||||||
from vbv_lernwelt.course_session_group.models import CourseSessionGroup
|
from vbv_lernwelt.dashboard.person_export import export_persons, PERSONS_EXPORT_FILENAME
|
||||||
|
from vbv_lernwelt.dashboard.utils import (
|
||||||
|
CourseSessionWithRoles,
|
||||||
|
create_course_session_dict,
|
||||||
|
create_person_list_with_roles,
|
||||||
|
get_course_sessions_with_roles_for_user,
|
||||||
|
user_role,
|
||||||
|
)
|
||||||
from vbv_lernwelt.duedate.models import DueDate
|
from vbv_lernwelt.duedate.models import DueDate
|
||||||
from vbv_lernwelt.duedate.serializers import DueDateSerializer
|
from vbv_lernwelt.duedate.serializers import DueDateSerializer
|
||||||
from vbv_lernwelt.feedback.export import (
|
from vbv_lernwelt.feedback.export import (
|
||||||
export_feedback_with_circle_restriction,
|
export_feedback_with_circle_restriction,
|
||||||
FEEDBACK_EXPORT_FILE_NAME,
|
FEEDBACK_EXPORT_FILE_NAME,
|
||||||
)
|
)
|
||||||
from vbv_lernwelt.learning_mentor.models import LearningMentor
|
|
||||||
from vbv_lernwelt.learnpath.models import Circle
|
from vbv_lernwelt.learnpath.models import Circle
|
||||||
from vbv_lernwelt.self_evaluation_feedback.models import SelfEvaluationFeedback
|
from vbv_lernwelt.self_evaluation_feedback.models import SelfEvaluationFeedback
|
||||||
|
|
||||||
|
|
@ -65,19 +67,6 @@ class RoleKeyType(Enum):
|
||||||
TRAINER = "Trainer"
|
TRAINER = "Trainer"
|
||||||
|
|
||||||
|
|
||||||
@dataclass(frozen=True)
|
|
||||||
class CourseSessionWithRoles:
|
|
||||||
_original: CourseSession
|
|
||||||
roles: Set[str]
|
|
||||||
|
|
||||||
def __getattr__(self, name: str):
|
|
||||||
# Delegate attribute access to the _original CourseSession object
|
|
||||||
return getattr(self._original, name)
|
|
||||||
|
|
||||||
def save(self, *args, **kwargs):
|
|
||||||
raise NotImplementedError("This proxy object cannot be saved.")
|
|
||||||
|
|
||||||
|
|
||||||
@dataclass(frozen=True)
|
@dataclass(frozen=True)
|
||||||
class CourseConfig:
|
class CourseConfig:
|
||||||
course_id: str
|
course_id: str
|
||||||
|
|
@ -92,156 +81,6 @@ class CourseConfig:
|
||||||
session_to_continue_id: str | None
|
session_to_continue_id: str | None
|
||||||
|
|
||||||
|
|
||||||
def get_course_sessions_with_roles_for_user(user: User) -> List[CourseSessionWithRoles]:
|
|
||||||
result_course_sessions = {}
|
|
||||||
|
|
||||||
# participant/member/expert course sessions
|
|
||||||
csu_qs = CourseSessionUser.objects.filter(user=user).prefetch_related(
|
|
||||||
"course_session", "course_session__course"
|
|
||||||
)
|
|
||||||
for csu in csu_qs:
|
|
||||||
cs = csu.course_session
|
|
||||||
# member/expert is mutually exclusive...
|
|
||||||
cs.roles = {csu.role}
|
|
||||||
result_course_sessions[cs.id] = cs
|
|
||||||
|
|
||||||
# enrich with supervisor course sessions
|
|
||||||
csg_qs = CourseSessionGroup.objects.filter(supervisor=user).prefetch_related(
|
|
||||||
"course_session", "course_session__course"
|
|
||||||
)
|
|
||||||
for csg in csg_qs:
|
|
||||||
for cs in csg.course_session.all():
|
|
||||||
cs.roles = set()
|
|
||||||
cs = result_course_sessions.get(cs.id, cs)
|
|
||||||
|
|
||||||
cs.roles.add("SUPERVISOR")
|
|
||||||
result_course_sessions[cs.id] = cs
|
|
||||||
|
|
||||||
# enrich with mentor course sessions
|
|
||||||
lm_qs = LearningMentor.objects.filter(mentor=user).prefetch_related(
|
|
||||||
"course_session", "course_session__course"
|
|
||||||
)
|
|
||||||
for lm in lm_qs:
|
|
||||||
cs = lm.course_session
|
|
||||||
cs.roles = set()
|
|
||||||
cs = result_course_sessions.get(cs.id, cs)
|
|
||||||
|
|
||||||
cs.roles.add("LEARNING_MENTOR")
|
|
||||||
result_course_sessions[cs.id] = cs
|
|
||||||
|
|
||||||
return [
|
|
||||||
CourseSessionWithRoles(cs, cs.roles) for cs in result_course_sessions.values()
|
|
||||||
]
|
|
||||||
|
|
||||||
|
|
||||||
def has_cs_role(roles: Set[str]) -> bool:
|
|
||||||
return bool(roles & {"SUPERVISOR", "EXPERT", "MEMBER"})
|
|
||||||
|
|
||||||
|
|
||||||
def user_role(roles: Set[str]) -> str:
|
|
||||||
if "SUPERVISOR" in roles:
|
|
||||||
return "SUPERVISOR"
|
|
||||||
if "EXPERT" in roles:
|
|
||||||
return "EXPERT"
|
|
||||||
if "MEMBER" in roles:
|
|
||||||
return "MEMBER"
|
|
||||||
return "LEARNING_MENTOR"
|
|
||||||
|
|
||||||
|
|
||||||
def _create_course_session_dict(course_session_object, my_role, user_role):
|
|
||||||
return {
|
|
||||||
"id": str(course_session_object.id),
|
|
||||||
"session_title": course_session_object.title,
|
|
||||||
"course_id": str(course_session_object.course.id),
|
|
||||||
"course_title": course_session_object.course.title,
|
|
||||||
"course_slug": course_session_object.course.slug,
|
|
||||||
"region": course_session_object.region,
|
|
||||||
"generation": course_session_object.generation,
|
|
||||||
"my_role": my_role,
|
|
||||||
"user_role": user_role,
|
|
||||||
"is_uk": course_session_object.course.configuration.is_uk,
|
|
||||||
"is_vv": course_session_object.course.configuration.is_vv,
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
def _create_person_list_with_roles(user):
|
|
||||||
def create_user_dict(user_object):
|
|
||||||
return {
|
|
||||||
"user_id": user_object.id,
|
|
||||||
"first_name": user_object.first_name,
|
|
||||||
"last_name": user_object.last_name,
|
|
||||||
"email": user_object.email,
|
|
||||||
"avatar_url_small": user_object.avatar_url_small,
|
|
||||||
"avatar_url": user_object.avatar_url,
|
|
||||||
"course_sessions": [],
|
|
||||||
}
|
|
||||||
|
|
||||||
course_sessions = get_course_sessions_with_roles_for_user(user)
|
|
||||||
|
|
||||||
result_persons = {}
|
|
||||||
for cs in course_sessions:
|
|
||||||
if has_cs_role(cs.roles) and cs.course.configuration.is_uk:
|
|
||||||
course_session_users = CourseSessionUser.objects.filter(
|
|
||||||
course_session=cs.id
|
|
||||||
).select_related("user")
|
|
||||||
my_role = user_role(cs.roles)
|
|
||||||
for csu in course_session_users:
|
|
||||||
person_data = result_persons.get(
|
|
||||||
csu.user.id, create_user_dict(csu.user)
|
|
||||||
)
|
|
||||||
person_data["course_sessions"].append(
|
|
||||||
_create_course_session_dict(cs, my_role, csu.role)
|
|
||||||
)
|
|
||||||
result_persons[csu.user.id] = person_data
|
|
||||||
|
|
||||||
# add persons where request.user is mentor
|
|
||||||
for cs in course_sessions:
|
|
||||||
if "LEARNING_MENTOR" in cs.roles:
|
|
||||||
lm = LearningMentor.objects.filter(
|
|
||||||
mentor=user, course_session=cs.id
|
|
||||||
).first()
|
|
||||||
|
|
||||||
for participant in lm.participants.all():
|
|
||||||
course_session_entry = _create_course_session_dict(
|
|
||||||
cs,
|
|
||||||
"LEARNING_MENTOR",
|
|
||||||
"LEARNING_MENTEE",
|
|
||||||
)
|
|
||||||
|
|
||||||
if participant.user.id not in result_persons:
|
|
||||||
person_data = create_user_dict(participant.user)
|
|
||||||
person_data["course_sessions"] = [course_session_entry]
|
|
||||||
result_persons[participant.user.id] = person_data
|
|
||||||
else:
|
|
||||||
# user is already in result_persons
|
|
||||||
result_persons[participant.user.id]["course_sessions"].append(
|
|
||||||
course_session_entry
|
|
||||||
)
|
|
||||||
|
|
||||||
# add persons where request.user is mentee
|
|
||||||
mentor_relation_qs = LearningMentor.objects.filter(
|
|
||||||
participants__user=user
|
|
||||||
).prefetch_related("mentor", "course_session")
|
|
||||||
for mentor_relation in mentor_relation_qs:
|
|
||||||
cs = mentor_relation.course_session
|
|
||||||
course_session_entry = _create_course_session_dict(
|
|
||||||
cs,
|
|
||||||
"LEARNING_MENTEE",
|
|
||||||
"LEARNING_MENTOR",
|
|
||||||
)
|
|
||||||
|
|
||||||
if mentor_relation.mentor.id not in result_persons:
|
|
||||||
person_data = create_user_dict(mentor_relation.mentor)
|
|
||||||
person_data["course_sessions"] = [course_session_entry]
|
|
||||||
result_persons[mentor_relation.mentor.id] = person_data
|
|
||||||
else:
|
|
||||||
# user is already in result_persons
|
|
||||||
result_persons[mentor_relation.mentor.id]["course_sessions"].append(
|
|
||||||
course_session_entry
|
|
||||||
)
|
|
||||||
return result_persons.values()
|
|
||||||
|
|
||||||
|
|
||||||
def _persons_list_add_competence_metrics(persons):
|
def _persons_list_add_competence_metrics(persons):
|
||||||
course_session_ids = {cs["id"] for p in persons for cs in p["course_sessions"]}
|
course_session_ids = {cs["id"] for p in persons for cs in p["course_sessions"]}
|
||||||
competence_assignments = query_competence_course_session_assignments(
|
competence_assignments = query_competence_course_session_assignments(
|
||||||
|
|
@ -284,7 +123,7 @@ def _persons_list_add_competence_metrics(persons):
|
||||||
@api_view(["GET"])
|
@api_view(["GET"])
|
||||||
def get_dashboard_persons(request):
|
def get_dashboard_persons(request):
|
||||||
try:
|
try:
|
||||||
persons = list(_create_person_list_with_roles(request.user))
|
persons = list(create_person_list_with_roles(request.user))
|
||||||
|
|
||||||
if request.GET.get("with_competence_metrics", "") == "true":
|
if request.GET.get("with_competence_metrics", "") == "true":
|
||||||
persons = _persons_list_add_competence_metrics(persons)
|
persons = _persons_list_add_competence_metrics(persons)
|
||||||
|
|
@ -322,7 +161,7 @@ def get_dashboard_due_dates(request):
|
||||||
cs = course_session_map.get(due_date.course_session_id)
|
cs = course_session_map.get(due_date.course_session_id)
|
||||||
|
|
||||||
if cs:
|
if cs:
|
||||||
data["course_session"] = _create_course_session_dict(
|
data["course_session"] = create_course_session_dict(
|
||||||
cs, my_role=user_role(cs.roles), user_role=""
|
cs, my_role=user_role(cs.roles), user_role=""
|
||||||
)
|
)
|
||||||
result_due_dates.append(data)
|
result_due_dates.append(data)
|
||||||
|
|
@ -577,6 +416,20 @@ def export_feedback_as_xsl(request):
|
||||||
return _make_excel_response(data, FEEDBACK_EXPORT_FILE_NAME)
|
return _make_excel_response(data, FEEDBACK_EXPORT_FILE_NAME)
|
||||||
|
|
||||||
|
|
||||||
|
@api_view(["POST"])
|
||||||
|
def export_persons_as_xsl(request):
|
||||||
|
requested_course_session_ids = request.data.get("courseSessionIds", [])
|
||||||
|
course_sessions_with_roles = _get_permitted_courses_sessions_for_user(
|
||||||
|
request.user, requested_course_session_ids
|
||||||
|
) # noqa
|
||||||
|
|
||||||
|
data = export_persons(
|
||||||
|
request.user,
|
||||||
|
[cswr.id for cswr in course_sessions_with_roles],
|
||||||
|
)
|
||||||
|
return _make_excel_response(data, PERSONS_EXPORT_FILENAME)
|
||||||
|
|
||||||
|
|
||||||
def _get_permitted_courses_sessions_for_user(
|
def _get_permitted_courses_sessions_for_user(
|
||||||
user: User, requested_coursesession_ids: List[str]
|
user: User, requested_coursesession_ids: List[str]
|
||||||
) -> List[CourseSessionWithRoles]:
|
) -> List[CourseSessionWithRoles]:
|
||||||
|
|
|
||||||
Loading…
Reference in New Issue