Merge branch 'develop' into feat/geteilter-bereich
This commit is contained in:
commit
3eb098be35
|
|
@ -0,0 +1,36 @@
|
|||
<script setup lang="ts">
|
||||
import ItButton from "@/components/ui/ItButton.vue";
|
||||
import type { Assignment } from "@/types";
|
||||
|
||||
const props = defineProps<{
|
||||
assignment: Assignment;
|
||||
}>();
|
||||
|
||||
const openSolutionSample = () => {
|
||||
const url = props.assignment.solution_sample?.url;
|
||||
if (url) {
|
||||
window.open(url, "_blank");
|
||||
}
|
||||
};
|
||||
</script>
|
||||
|
||||
<template>
|
||||
<div v-if="assignment.solution_sample" data-cy="show-sample-solution">
|
||||
<p>
|
||||
{{ $t("assignment.submissionShowSampleSolutionText") }}
|
||||
</p>
|
||||
|
||||
<ItButton
|
||||
class="mt-6"
|
||||
variant="primary"
|
||||
size="normal"
|
||||
:disabled="false"
|
||||
data-cy="show-sample-solution-button"
|
||||
@click="openSolutionSample"
|
||||
>
|
||||
<p>{{ $t("assignment.submissionShowSampleSolution") }}</p>
|
||||
</ItButton>
|
||||
</div>
|
||||
</template>
|
||||
|
||||
<style scoped></style>
|
||||
|
|
@ -14,6 +14,7 @@ import type { Assignment } from "@/types";
|
|||
import DateEmbedding from "@/components/dueDates/DateEmbedding.vue";
|
||||
import { useMyLearningMentors } from "@/composables";
|
||||
import NoMentorInformationPanel from "@/components/learningMentor/NoMentorInformationPanel.vue";
|
||||
import SampleSolution from "@/components/assignment/SampleSolution.vue";
|
||||
|
||||
const props = defineProps<{
|
||||
submissionDeadlineStart?: string | null;
|
||||
|
|
@ -98,4 +99,9 @@ const onSubmit = async () => {
|
|||
>
|
||||
<p>{{ $t("a.Ergebnisse teilen") }}</p>
|
||||
</ItButton>
|
||||
<SampleSolution
|
||||
v-if="learningMentors?.length == 0"
|
||||
class="pt-8"
|
||||
:assignment="assignment"
|
||||
/>
|
||||
</template>
|
||||
|
|
|
|||
|
|
@ -13,11 +13,11 @@ export function assignmentsMaxEvaluationPoints(
|
|||
}
|
||||
|
||||
export function assignmentsUserPoints(assignments: CompetenceCertificateAssignment[]) {
|
||||
return _.sum(
|
||||
return +_.sum(
|
||||
assignments
|
||||
.filter((a) => a.completion?.completion_status === "EVALUATION_SUBMITTED")
|
||||
.map((a) => a.completion?.evaluation_points ?? 0)
|
||||
);
|
||||
).toFixed(1);
|
||||
}
|
||||
|
||||
export function competenceCertificateProgressStatusCount(
|
||||
|
|
|
|||
|
|
@ -41,7 +41,6 @@ const someFinished = computed(() => {
|
|||
if (props.learningSequence) {
|
||||
return someFinishedInLearningSequence(props.learningSequence);
|
||||
}
|
||||
|
||||
return false;
|
||||
});
|
||||
|
||||
|
|
@ -235,7 +234,11 @@ type LearninContentWithCompetenceCertificate =
|
|||
: 'no-status'
|
||||
"
|
||||
>
|
||||
<CompletionStatus />
|
||||
<CompletionStatus
|
||||
:completed="
|
||||
performanceCriteriaHasStatus(learningUnit.performance_criteria)
|
||||
"
|
||||
/>
|
||||
{{ $t("a.Selbsteinschätzung") }}
|
||||
</div>
|
||||
|
||||
|
|
|
|||
|
|
@ -1,5 +1,4 @@
|
|||
<script setup lang="ts">
|
||||
import ItButton from "@/components/ui/ItButton.vue";
|
||||
import ItSuccessAlert from "@/components/ui/ItSuccessAlert.vue";
|
||||
import {
|
||||
useCourseData,
|
||||
|
|
@ -14,6 +13,7 @@ import type { AssignmentAssignmentAssignmentTypeChoices } from "@/gql/graphql";
|
|||
import CaseWorkSubmit from "@/components/learningPath/assignment/CaseWorkSubmit.vue";
|
||||
import SimpleSubmit from "@/components/learningPath/assignment/SimpleSubmit.vue";
|
||||
import PraxisAssignmentSubmit from "@/components/learningPath/assignment/PraxisAssignmentSubmit.vue";
|
||||
import SampleSolution from "@/components/assignment/SampleSolution.vue";
|
||||
|
||||
const props = defineProps<{
|
||||
assignment: Assignment;
|
||||
|
|
@ -78,14 +78,6 @@ const isPraxisAssignment = computed(() => checkAssignmentType(["PRAXIS_ASSIGNMEN
|
|||
const onEditTask = (task: AssignmentTask) => {
|
||||
emit("editTask", task);
|
||||
};
|
||||
|
||||
const openSolutionSample = () => {
|
||||
const url = props.assignment.solution_sample?.url ?? "";
|
||||
|
||||
if (props.assignment.solution_sample) {
|
||||
window.open(url, "_blank");
|
||||
}
|
||||
};
|
||||
</script>
|
||||
<template>
|
||||
<div class="w-full border border-gray-400 p-8" data-cy="confirm-container">
|
||||
|
|
@ -134,26 +126,7 @@ const openSolutionSample = () => {
|
|||
$t("assignment.submissionNotificationDisclaimer", { name: circleExpertName })
|
||||
}}
|
||||
</p>
|
||||
<div
|
||||
v-if="assignment.solution_sample"
|
||||
class="pt-2"
|
||||
data-cy="show-sample-solution"
|
||||
>
|
||||
<p>
|
||||
{{ $t("assignment.submissionShowSampleSolutionText") }}
|
||||
</p>
|
||||
|
||||
<ItButton
|
||||
class="mt-6"
|
||||
variant="primary"
|
||||
size="normal"
|
||||
:disabled="false"
|
||||
data-cy="show-sample-solution-button"
|
||||
@click="openSolutionSample"
|
||||
>
|
||||
<p>{{ $t("assignment.submissionShowSampleSolution") }}</p>
|
||||
</ItButton>
|
||||
</div>
|
||||
<SampleSolution class="pt-2" :assignment="assignment" />
|
||||
</div>
|
||||
</div>
|
||||
<AssignmentSubmissionResponses
|
||||
|
|
|
|||
|
|
@ -6,10 +6,9 @@ const props = defineProps<{
|
|||
content: LearningContent;
|
||||
}>();
|
||||
</script>
|
||||
|
||||
<template>
|
||||
<LearningContentSimpleLayout :learning-content="props.content">
|
||||
<div class="h-screen">
|
||||
<div class="h-[calc(100vh-222px)] md:h-[calc(100vh-238px)]">
|
||||
<iframe width="100%" height="100%" :src="props.content.content_url" />
|
||||
</div>
|
||||
</LearningContentSimpleLayout>
|
||||
|
|
|
|||
|
|
@ -43,10 +43,24 @@ export function someFinishedInLearningSequence(ls: LearningSequence) {
|
|||
});
|
||||
}
|
||||
|
||||
/**
|
||||
* Completed is defined as having completed all learning contents, ranked at
|
||||
* least one performance criteria (but not necessarily all) as success or fail.
|
||||
*/
|
||||
export function allFinishedInLearningSequence(ls: LearningSequence) {
|
||||
return learningSequenceFlatChildren(ls).every((lc) => {
|
||||
return lc.completion_status === "SUCCESS";
|
||||
});
|
||||
const allLearningContentsCompleted = ls.learning_units
|
||||
.flatMap((lu) => lu.learning_contents)
|
||||
.every((lc) => lc.completion_status === "SUCCESS");
|
||||
|
||||
const performanceCriteria = ls.learning_units.flatMap(
|
||||
(lu) => lu.performance_criteria
|
||||
);
|
||||
|
||||
const somePerformanceCriteriaRanked =
|
||||
performanceCriteriaHasStatus(performanceCriteria) ||
|
||||
performanceCriteria.length === 0; // -> treat as completed
|
||||
|
||||
return allLearningContentsCompleted && somePerformanceCriteriaRanked;
|
||||
}
|
||||
|
||||
export function performanceCriteriaStatusCount(
|
||||
|
|
|
|||
Binary file not shown.
|
|
@ -9,6 +9,7 @@ from vbv_lernwelt.assignment.models import (
|
|||
AssignmentCompletion,
|
||||
AssignmentCompletionAuditLog,
|
||||
AssignmentCompletionStatus,
|
||||
AssignmentType,
|
||||
is_valid_assignment_completion_status,
|
||||
)
|
||||
from vbv_lernwelt.core.models import User
|
||||
|
|
@ -208,10 +209,14 @@ def update_assignment_completion(
|
|||
|
||||
ac.save()
|
||||
|
||||
if completion_status in [
|
||||
AssignmentCompletionStatus.EVALUATION_SUBMITTED,
|
||||
AssignmentCompletionStatus.SUBMITTED,
|
||||
]:
|
||||
if (
|
||||
completion_status
|
||||
in [
|
||||
AssignmentCompletionStatus.EVALUATION_SUBMITTED,
|
||||
AssignmentCompletionStatus.SUBMITTED,
|
||||
]
|
||||
and assignment.assignment_type != AssignmentType.EDONIQ_TEST.value
|
||||
):
|
||||
acl = AssignmentCompletionAuditLog.objects.create(
|
||||
assignment_user=assignment_user,
|
||||
assignment=assignment,
|
||||
|
|
|
|||
|
|
@ -1,3 +1,4 @@
|
|||
# Course IDs
|
||||
COURSE_TEST_ID = -1
|
||||
COURSE_UK = -3
|
||||
COURSE_VERSICHERUNGSVERMITTLERIN_ID = -4
|
||||
|
|
|
|||
|
|
@ -88,9 +88,10 @@ def create_user(username: str) -> User:
|
|||
|
||||
|
||||
def create_course_session(
|
||||
course: Course, title: str, generation: str = "2023"
|
||||
course: Course, title: str, generation: str = "2023", _id=None
|
||||
) -> CourseSession:
|
||||
return CourseSession.objects.create(
|
||||
id=_id,
|
||||
course=course,
|
||||
title=title,
|
||||
import_id=title,
|
||||
|
|
|
|||
|
|
@ -185,7 +185,7 @@ def export_feedback(course_session_ids: list[str], save_as_file: bool):
|
|||
|
||||
|
||||
def _create_sheet(wb: Workbook, title: str, data: list[FeedbackResponse]):
|
||||
sheet = wb.create_sheet(title=title)
|
||||
sheet = wb.create_sheet(title=_sanitize_sheet_name(title))
|
||||
|
||||
if len(data) == 0:
|
||||
return sheet
|
||||
|
|
@ -208,6 +208,24 @@ def _create_sheet(wb: Workbook, title: str, data: list[FeedbackResponse]):
|
|||
return sheet
|
||||
|
||||
|
||||
def _sanitize_sheet_name(text, default_name="DefaultSheet"):
|
||||
if text is None:
|
||||
return default_name
|
||||
|
||||
prohibited_chars = ["\\", "/", "*", "?", ":", "[", "]"]
|
||||
for char in prohibited_chars:
|
||||
text = text.replace(char, "")
|
||||
|
||||
text = text.strip("'")
|
||||
|
||||
text = text[:31]
|
||||
|
||||
if len(text) == 0:
|
||||
return default_name
|
||||
|
||||
return text
|
||||
|
||||
|
||||
def _add_rows(sheet, data, question_data):
|
||||
for row_idx, feedback in enumerate(data, start=2):
|
||||
sheet.cell(row=row_idx, column=1, value=feedback.course_session.title)
|
||||
|
|
|
|||
|
|
@ -35,7 +35,13 @@ class DatatransWebhookTestCase(APITestCase):
|
|||
_id=COURSE_VERSICHERUNGSVERMITTLERIN_ID,
|
||||
)
|
||||
|
||||
create_course_session(course=course, title="Versicherungsvermittler/-in DE")
|
||||
create_course_session(
|
||||
course=course,
|
||||
title="Versicherungsvermittler/-in DE",
|
||||
# MUST be 1 -> this is the correct
|
||||
# VV DE course session ID in production
|
||||
_id=1,
|
||||
)
|
||||
|
||||
self.user = User.objects.create_user(
|
||||
username="testuser",
|
||||
|
|
@ -145,14 +151,21 @@ class DatatransWebhookTestCase(APITestCase):
|
|||
CourseSessionUser.objects.count(),
|
||||
)
|
||||
|
||||
csu = CourseSessionUser.objects.first()
|
||||
|
||||
self.assertEqual(
|
||||
self.user,
|
||||
CourseSessionUser.objects.first().user,
|
||||
csu.user,
|
||||
)
|
||||
|
||||
self.assertEqual(
|
||||
1,
|
||||
csu.course_session.id,
|
||||
)
|
||||
|
||||
self.assertEqual(
|
||||
COURSE_VERSICHERUNGSVERMITTLERIN_ID,
|
||||
CourseSessionUser.objects.first().course_session.course.id,
|
||||
csu.course_session.course.id,
|
||||
)
|
||||
|
||||
self.assertEqual(
|
||||
|
|
|
|||
|
|
@ -8,11 +8,6 @@ from rest_framework.response import Response
|
|||
from sentry_sdk import capture_exception
|
||||
|
||||
from vbv_lernwelt.core.models import Country, User
|
||||
from vbv_lernwelt.course.consts import (
|
||||
COURSE_VERSICHERUNGSVERMITTLERIN_FR_ID,
|
||||
COURSE_VERSICHERUNGSVERMITTLERIN_ID,
|
||||
COURSE_VERSICHERUNGSVERMITTLERIN_IT_ID,
|
||||
)
|
||||
from vbv_lernwelt.course.models import CourseSession, CourseSessionUser
|
||||
from vbv_lernwelt.notify.email.email_services import EmailTemplate, send_email
|
||||
from vbv_lernwelt.shop.const import (
|
||||
|
|
@ -37,10 +32,11 @@ from vbv_lernwelt.shop.services import (
|
|||
|
||||
logger = structlog.get_logger(__name__)
|
||||
|
||||
PRODUCT_SKU_TO_COURSE = {
|
||||
VV_DE_PRODUCT_SKU: COURSE_VERSICHERUNGSVERMITTLERIN_ID,
|
||||
VV_FR_PRODUCT_SKU: COURSE_VERSICHERUNGSVERMITTLERIN_FR_ID,
|
||||
VV_IT_PRODUCT_SKU: COURSE_VERSICHERUNGSVERMITTLERIN_IT_ID,
|
||||
PRODUCT_SKU_TO_COURSE_SESSION_ID = {
|
||||
# CourseSession IDs PROD/STAGING
|
||||
VV_DE_PRODUCT_SKU: 1, # vv-de
|
||||
VV_FR_PRODUCT_SKU: 2, # vv-fr
|
||||
VV_IT_PRODUCT_SKU: 3, # vv-it
|
||||
}
|
||||
|
||||
|
||||
|
|
@ -229,9 +225,9 @@ def create_vv_course_session_user(checkout_info: CheckoutInformation):
|
|||
_, created = CourseSessionUser.objects.get_or_create(
|
||||
user=checkout_info.user,
|
||||
role=CourseSessionUser.Role.MEMBER,
|
||||
course_session=CourseSession.objects.filter(
|
||||
course_id=PRODUCT_SKU_TO_COURSE[checkout_info.product_sku]
|
||||
).first(),
|
||||
course_session=CourseSession.objects.get(
|
||||
id=PRODUCT_SKU_TO_COURSE_SESSION_ID[checkout_info.product_sku]
|
||||
),
|
||||
)
|
||||
|
||||
if created:
|
||||
|
|
|
|||
Loading…
Reference in New Issue