diff --git a/client/src/pages/onboarding/vv/CheckoutAddress.vue b/client/src/pages/onboarding/vv/CheckoutAddress.vue
index 76e5a6ed..ba309415 100644
--- a/client/src/pages/onboarding/vv/CheckoutAddress.vue
+++ b/client/src/pages/onboarding/vv/CheckoutAddress.vue
@@ -265,11 +265,10 @@ const executePayment = async () => {
{{
- $t("a.Fehler bei der Zahlung. Bitte versuche es erneut oder kontaktiere uns")
- }}:
-
- vermittler@vbv-afa.ch
-
+ $t(
+ "a.Fehler bei der Zahlung. Bitte versuche es erneut oder wähle eine andere Zahlungsmethode."
+ )
+ }}
{{ $t("a.Adresse") }}
diff --git a/server/config/settings/base.py b/server/config/settings/base.py
index 031cb063..de7744aa 100644
--- a/server/config/settings/base.py
+++ b/server/config/settings/base.py
@@ -201,6 +201,7 @@ MIDDLEWARE = [
"vbv_lernwelt.core.middleware.security.SecurityRequestResponseLoggingMiddleware",
"wagtail.contrib.redirects.middleware.RedirectMiddleware",
"vbv_lernwelt.core.middleware.auth.UserLoggedInCookieMiddleWare",
+ # "vbv_lernwelt.debugtools.middleware.QueryCountDebugMiddleware",
]
# STATIC
diff --git a/server/vbv_lernwelt/competence/services.py b/server/vbv_lernwelt/competence/services.py
index 0f5cf691..a04bedc6 100644
--- a/server/vbv_lernwelt/competence/services.py
+++ b/server/vbv_lernwelt/competence/services.py
@@ -18,6 +18,11 @@ def query_competence_course_session_assignments(course_session_ids, circle_ids=N
],
learning_content__content_assignment__competence_certificate__isnull=False,
learning_content__live=True,
+ ).select_related(
+ "submission_deadline",
+ "learning_content",
+ "course_session",
+ "learning_content__content_assignment",
):
if circle_ids and csa.learning_content.get_circle().id not in circle_ids:
continue
@@ -36,6 +41,11 @@ def query_competence_course_session_edoniq_tests(course_session_ids, circle_ids=
course_session_id__in=course_session_ids,
learning_content__content_assignment__competence_certificate__isnull=False,
learning_content__live=True,
+ ).select_related(
+ "deadline",
+ "learning_content",
+ "course_session",
+ "learning_content__content_assignment",
):
if circle_ids and cset.learning_content.get_circle().id not in circle_ids:
continue
diff --git a/server/vbv_lernwelt/dashboard/graphql/types/assignment.py b/server/vbv_lernwelt/dashboard/graphql/types/assignment.py
index f2d36caa..1c614e72 100644
--- a/server/vbv_lernwelt/dashboard/graphql/types/assignment.py
+++ b/server/vbv_lernwelt/dashboard/graphql/types/assignment.py
@@ -1,5 +1,5 @@
import math
-from typing import List
+from typing import List, Tuple
import graphene
@@ -110,15 +110,23 @@ def get_assignment_completion_metrics(
assignment: vbv_lernwelt.assignment.models.Assignment,
user_selection_ids: List[str] | None,
urql_id_postfix: str = "",
+ context=None,
) -> AssignmentCompletionMetricsType:
+ if not context:
+ context = {}
+
csu_qs = CourseSessionUser.objects.filter(
course_session_id=course_session.id, role=CourseSessionUser.Role.MEMBER
)
- if user_selection_ids:
- csu_qs = csu_qs.filter(user_id__in=user_selection_ids)
+ key = f"CourseSessionUsers_{course_session.id}"
+ if not key in context:
+ if user_selection_ids:
+ csu_qs = csu_qs.filter(user_id__in=user_selection_ids)
- course_session_users = csu_qs.values_list("user", flat=True)
+ context[key] = list(csu_qs.values_list("user", flat=True))
+
+ course_session_users = context[key]
assignment_completions = AssignmentCompletion.objects.filter(
completion_status=AssignmentCompletionStatus.EVALUATION_SUBMITTED.value,
@@ -166,37 +174,59 @@ def create_record(
course_session_assignment: CourseSessionAssignment | CourseSessionEdoniqTest,
user_selection_ids: List[str] | None,
urql_id_postfix: str = "",
-) -> AssignmentStatisticsRecordType:
- if isinstance(course_session_assignment, CourseSessionAssignment):
- due_date = course_session_assignment.submission_deadline
- else:
- due_date = course_session_assignment.deadline
+ context=None,
+) -> Tuple[AssignmentStatisticsRecordType, dict]:
+ if not context:
+ context = {}
+
+ assignment_type = (
+ "CourseSessionAssignment"
+ if isinstance(course_session_assignment, CourseSessionAssignment)
+ else "CourseSessionEdoniqTest"
+ )
+ due_date = (
+ course_session_assignment.submission_deadline
+ if assignment_type == "CourseSessionAssignment"
+ else course_session_assignment.deadline
+ )
+
+ key = f"{assignment_type}_{course_session_assignment.learning_content.id}"
+
+ if not key in context:
+ context[key] = course_session_assignment.learning_content.get_circle().id
+
+ circle_id = context[key]
learning_content = course_session_assignment.learning_content
competence_certificate = learning_content.content_assignment.competence_certificate
- return AssignmentStatisticsRecordType(
- # make sure it's unique, across all types of assignments!
- _id=f"{course_session_assignment._meta.model_name}#{course_session_assignment.id}@{urql_id_postfix}", # noqa
- course_session_id=str(course_session_assignment.course_session.id), # noqa
- course_session_title=course_session_assignment.course_session.title, # noqa
- circle_id=learning_content.get_circle().id, # noqa
- course_session_assignment_id=str(course_session_assignment.id), # noqa
- generation=course_session_assignment.course_session.generation, # noqa
- region=course_session_assignment.course_session.region, # noqa
- assignment_type_translation_key=due_date.assignment_type_translation_key, # noqa
- competence_certificate_id=str(competence_certificate.id), # noqa
- competence_certificate_title=competence_certificate.title, # noqa
- assignment_title=learning_content.content_assignment.title, # noqa
- learning_content_id=str(learning_content.id), # noqa
- metrics=get_assignment_completion_metrics( # noqa
- course_session=course_session_assignment.course_session, # noqa
- assignment=learning_content.content_assignment, # noqa
- user_selection_ids=user_selection_ids, # noqa
- urql_id_postfix=urql_id_postfix, # noqa
+ return (
+ AssignmentStatisticsRecordType(
+ # make sure it's unique, across all types of assignments!
+ _id=f"{course_session_assignment._meta.model_name}#{course_session_assignment.id}@{urql_id_postfix}",
+ # noqa
+ course_session_id=str(course_session_assignment.course_session.id), # noqa
+ course_session_title=course_session_assignment.course_session.title, # noqa
+ circle_id=circle_id, # noqa
+ course_session_assignment_id=str(course_session_assignment.id), # noqa
+ generation=course_session_assignment.course_session.generation, # noqa
+ region=course_session_assignment.course_session.region, # noqa
+ assignment_type_translation_key=due_date.assignment_type_translation_key, # noqa
+ competence_certificate_id=str(competence_certificate.id), # noqa
+ competence_certificate_title=competence_certificate.title, # noqa
+ assignment_title=learning_content.content_assignment.title, # noqa
+ learning_content_id=str(learning_content.id), # noqa
+ metrics=get_assignment_completion_metrics( # noqa
+ course_session=course_session_assignment.course_session, # noqa
+ assignment=learning_content.content_assignment, # noqa
+ user_selection_ids=user_selection_ids, # noqa
+ urql_id_postfix=urql_id_postfix, # noqa
+ context=context, # noqa
+ ),
+ details_url=due_date.url_expert, # noqa
+ deadline=due_date.start, # noqa
),
- details_url=due_date.url_expert, # noqa
- deadline=due_date.start, # noqa
+ context,
)
@@ -209,28 +239,26 @@ def assignments(
) -> AssignmentsStatisticsType:
if urql_id is None:
urql_id = str(course_id)
-
course_sessions = CourseSession.objects.filter(
id__in=course_session_selection_ids,
)
records: List[AssignmentStatisticsRecordType] = []
+ context = {}
- for course_session in course_sessions:
- for csa in query_competence_course_session_assignments(
- [course_session.id], circle_ids
- ):
- record = create_record(csa, user_selection_ids, urql_id_postfix=urql_id)
- records.append(record)
+ csas = query_competence_course_session_assignments(course_sessions, circle_ids)
+ csets = query_competence_course_session_edoniq_tests(course_sessions, circle_ids)
- for cset in query_competence_course_session_edoniq_tests(
- [course_session.id], circle_ids
- ):
- record = create_record(
- course_session_assignment=cset,
- user_selection_ids=user_selection_ids,
- urql_id_postfix=urql_id,
- )
- records.append(record)
+ for csa in csas:
+ record, context = create_record(
+ csa, user_selection_ids, urql_id_postfix=urql_id, context=context
+ )
+ records.append(record)
+
+ for cset in csets:
+ record, context = create_record(
+ cset, user_selection_ids, urql_id_postfix=urql_id, context=context
+ )
+ records.append(record)
return AssignmentsStatisticsType(
_id=urql_id, # noqa
diff --git a/server/vbv_lernwelt/dashboard/graphql/types/competence.py b/server/vbv_lernwelt/dashboard/graphql/types/competence.py
index 4df3984e..2265ab5a 100644
--- a/server/vbv_lernwelt/dashboard/graphql/types/competence.py
+++ b/server/vbv_lernwelt/dashboard/graphql/types/competence.py
@@ -42,25 +42,23 @@ def competences(
completions = CourseCompletion.objects.filter(
course_session_id__in=course_session_selection_ids,
page_type="competence.PerformanceCriteria",
- ).prefetch_related("course_session", "page")
+ ).select_related("course_session", "page")
if user_selection_ids is not None:
completions = completions.filter(user_id__in=user_selection_ids)
- competence_records = {}
+ page_ids = completions.values_list("page_id", flat=True).distinct()
+ pages = Page.objects.filter(id__in=page_ids).specific()
+ learning_units = {page.id: page.specific.learning_unit for page in pages}
- unique_page_ids = {completion.page.id for completion in completions}
- learning_units = {
- page_id: Page.objects.get(id=page_id).specific.learning_unit
- for page_id in unique_page_ids
- }
circles = {
lu.id: c
for lu in learning_units.values()
- if (lu is not None and (c := lu.get_circle()) is not None)
- and (circle_ids is None or c.id in circle_ids)
+ if lu and (c := lu.get_circle()) and (circle_ids is None or c.id in circle_ids)
}
+ competence_records = {}
+
for completion in completions:
learning_unit = learning_units.get(completion.page.id)
diff --git a/server/vbv_lernwelt/dashboard/views.py b/server/vbv_lernwelt/dashboard/views.py
index 95c85a7b..d17b7300 100644
--- a/server/vbv_lernwelt/dashboard/views.py
+++ b/server/vbv_lernwelt/dashboard/views.py
@@ -188,7 +188,7 @@ def _create_person_list_with_roles(user):
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(
@@ -250,7 +250,6 @@ def _create_person_list_with_roles(user):
result_persons[mentor_relation.agent.id]["course_sessions"].append(
course_session_entry
)
-
return result_persons.values()
@@ -318,45 +317,28 @@ def get_dashboard_due_dates(request):
course_sessions = get_course_sessions_with_roles_for_user(request.user)
course_session_ids = [cs.id for cs in course_sessions]
- all_due_dates = DueDate.objects.filter(
- course_session__id__in=course_session_ids
- )
-
- # filter only future due dates
- due_dates = []
today = date.today()
- for due_date in all_due_dates:
- # due_dates.append(due_date)
- if due_date.end:
- if due_date.end.date() >= today:
- due_dates.append(due_date)
- elif due_date.start:
- if due_date.start.date() >= today:
- due_dates.append(due_date)
- due_dates.sort(key=lambda x: x.start)
-
- # find course session by id in `course_sessions`
+ # Fetch future due dates in a single query using Q objects for complex filtering
+ future_due_dates = DueDate.objects.filter(
+ Q(course_session_id__in=course_session_ids),
+ Q(end__gte=today) | Q(start__gte=today),
+ ).select_related("course_session")
result_due_dates = []
- for due_date in due_dates:
- data = DueDateSerializer(due_date).data
+ course_session_map = {cs.id: cs for cs in course_sessions}
+
+ for due_date in sorted(future_due_dates, key=lambda x: x.start):
+ data = DueDateSerializer(due_date).data
+ cs = course_session_map.get(due_date.course_session_id)
- cs = next(
- course_session
- for course_session in course_sessions
- if course_session.id == due_date.course_session.id
- )
if cs:
data["course_session"] = _create_course_session_dict(
cs, my_role=user_role(cs.roles), user_role=""
)
result_due_dates.append(data)
- return Response(
- status=200,
- data=result_due_dates,
- )
+ return Response(status=200, data=result_due_dates)
except PermissionDenied as e:
raise e
diff --git a/server/vbv_lernwelt/debugtools/middleware.py b/server/vbv_lernwelt/debugtools/middleware.py
new file mode 100644
index 00000000..fea26453
--- /dev/null
+++ b/server/vbv_lernwelt/debugtools/middleware.py
@@ -0,0 +1,38 @@
+import time
+
+import structlog
+from django.db import connection, reset_queries
+
+logger = structlog.get_logger(__name__)
+
+
+class QueryCountDebugMiddleware:
+ def __init__(self, get_response):
+ self.get_response = get_response
+
+ def __call__(self, request):
+ if not request.path.startswith("/api/") and not request.path.startswith(
+ "/server/"
+ ):
+ return self.get_response(request)
+
+ reset_queries()
+ start_queries = len(connection.queries)
+ start_time = time.time()
+
+ response = self.get_response(request)
+
+ end_queries = len(connection.queries)
+ end_time = time.time()
+
+ total_queries = end_queries - start_queries
+ duration = end_time - start_time
+
+ logger.debug(
+ "query_count_middleware",
+ request_path=request.path,
+ queries=total_queries,
+ duration=duration,
+ )
+
+ return response