343 lines
12 KiB
Python
343 lines
12 KiB
Python
from typing import Dict, List, Set, Tuple
|
|
|
|
import graphene
|
|
|
|
from vbv_lernwelt.assignment.models import (
|
|
AssignmentCompletion,
|
|
AssignmentCompletionStatus,
|
|
)
|
|
from vbv_lernwelt.core.admin import User
|
|
from vbv_lernwelt.course.consts import UK_COURSE_IDS
|
|
from vbv_lernwelt.course.models import Course, CourseSession, CourseSessionUser
|
|
from vbv_lernwelt.course_session_group.models import CourseSessionGroup
|
|
from vbv_lernwelt.dashboard.graphql.types.competence import competences
|
|
from vbv_lernwelt.dashboard.graphql.types.dashboard import (
|
|
BaseStatisticsType,
|
|
CourseProgressType,
|
|
CourseStatisticsType,
|
|
DashboardConfigType,
|
|
DashboardType,
|
|
ProgressDashboardAssignmentType,
|
|
ProgressDashboardCompetenceType,
|
|
)
|
|
from vbv_lernwelt.iam.permissions import (
|
|
can_view_course_session,
|
|
can_view_course_session_group_statistics,
|
|
can_view_course_session_progress,
|
|
)
|
|
from vbv_lernwelt.learning_mentor.models import (
|
|
AgentParticipantRelation,
|
|
AgentParticipantRoleType,
|
|
)
|
|
from vbv_lernwelt.learnpath.models import Circle
|
|
|
|
|
|
def _agent_course_statistics(user, course_id: str, role: str):
|
|
course = Course.objects.get(id=course_id)
|
|
|
|
participant_ids = set()
|
|
course_session_ids = set()
|
|
|
|
relations_qs = AgentParticipantRelation.objects.filter(
|
|
agent=user,
|
|
role=role,
|
|
participant__course_session__course=course,
|
|
)
|
|
|
|
for relation in relations_qs:
|
|
participant_ids.add(relation.participant.user_id)
|
|
course_session_ids.add(relation.participant.course_session_id)
|
|
|
|
return CourseStatisticsType(
|
|
_id=f"{role}:{course.id}", # noqa
|
|
course_id=course.id, # noqa
|
|
course_title=course.title, # noqa
|
|
course_slug=course.slug, # noqa
|
|
course_session_selection_ids=list(course_session_ids), # noqa
|
|
user_selection_ids=list(participant_ids), # noqa
|
|
)
|
|
|
|
|
|
class DashboardQuery(graphene.ObjectType):
|
|
course_statistics = graphene.Field(
|
|
CourseStatisticsType, course_id=graphene.ID(required=True)
|
|
)
|
|
|
|
mentor_course_statistics = graphene.Field(
|
|
BaseStatisticsType,
|
|
course_id=graphene.ID(required=True),
|
|
agent_role=graphene.String(required=True),
|
|
)
|
|
|
|
course_progress = graphene.Field(
|
|
CourseProgressType, course_id=graphene.ID(required=True)
|
|
)
|
|
|
|
dashboard_config = graphene.List(
|
|
graphene.NonNull(DashboardConfigType), required=True
|
|
)
|
|
|
|
def resolve_course_statistics(root, info, course_id: str): # noqa
|
|
user = info.context.user
|
|
course = Course.objects.get(id=course_id)
|
|
|
|
course_session_ids = set()
|
|
|
|
# supervisors
|
|
for group in CourseSessionGroup.objects.filter(course=course):
|
|
if can_view_course_session_group_statistics(user=user, group=group):
|
|
course_session_ids.update(
|
|
group.course_session.all().values_list("id", flat=True)
|
|
)
|
|
|
|
# let's assume that users are not supervisors in one region/group and trainer in another
|
|
if not course_session_ids:
|
|
circle_ids = set()
|
|
circle_ids.update(
|
|
Circle.objects.filter(
|
|
expert__user=user, expert__role=CourseSessionUser.Role.EXPERT
|
|
).values_list("id", flat=True)
|
|
)
|
|
|
|
if not circle_ids:
|
|
return None
|
|
|
|
course_session_ids = CourseSession.objects.filter(
|
|
course=course,
|
|
coursesessionuser__user=user,
|
|
coursesessionuser__role=CourseSessionUser.Role.EXPERT,
|
|
).values_list("id", flat=True)
|
|
setattr(info.context, "circle_ids", list(circle_ids)) # noqa: B010
|
|
|
|
# todo: if course_session_ids and circles are empty return none or 404 or 401
|
|
|
|
return CourseStatisticsType(
|
|
_id=course.id, # noqa
|
|
course_id=course.id, # noqa
|
|
course_title=course.title, # noqa
|
|
course_slug=course.slug, # noqa
|
|
course_session_selection_ids=list(course_session_ids), # noqa
|
|
)
|
|
|
|
def resolve_mentor_course_statistics(root, info, course_id: str, agent_role: str): # noqa
|
|
user = info.context.user
|
|
|
|
return _agent_course_statistics(user, course_id, role=agent_role)
|
|
|
|
def resolve_dashboard_config(root, info): # noqa
|
|
user = info.context.user
|
|
|
|
if user.is_superuser:
|
|
return [
|
|
{
|
|
"id": c.id,
|
|
"course_id": c.id,
|
|
"name": c.title,
|
|
"slug": c.slug,
|
|
"dashboard_type": DashboardType.SIMPLE_DASHBOARD,
|
|
"course_configuration": c.configuration,
|
|
}
|
|
for c in Course.objects.all()
|
|
]
|
|
|
|
(
|
|
statistic_dashboards,
|
|
statistics_dashboard_course_ids,
|
|
) = get_user_statistics_dashboards(user=user)
|
|
|
|
(
|
|
course_session_dashboards,
|
|
course_session_dashboard_course_ids,
|
|
) = get_user_course_session_dashboards(
|
|
user=user, exclude_course_ids=statistics_dashboard_course_ids
|
|
)
|
|
|
|
learning_mentor_dashboards, _ = get_learning_mentor_dashboards(
|
|
user=user, exclude_course_ids=course_session_dashboard_course_ids
|
|
)
|
|
|
|
return (
|
|
statistic_dashboards
|
|
+ course_session_dashboards
|
|
+ learning_mentor_dashboards
|
|
)
|
|
|
|
def resolve_course_progress(root, info, course_id: str): # noqa
|
|
"""
|
|
Slightly fragile but could be good enough: most only have one
|
|
course session per course anyway but if there are multiple, we
|
|
just pick the newest one (by generation) as best guess.
|
|
"""
|
|
|
|
user = info.context.user
|
|
course = Course.objects.get(id=course_id)
|
|
setattr(info.context, "course", course) # noqa: B010
|
|
|
|
newest: CourseSession | None = None
|
|
course_session_for_user: List[str] = []
|
|
|
|
# generation
|
|
for course_session in CourseSession.objects.filter(course_id=course_id):
|
|
if can_view_course_session_progress(
|
|
user=user, course_session=course_session
|
|
):
|
|
course_session_for_user.append(course_session)
|
|
generation_newest = newest.generation if newest else None
|
|
if (
|
|
generation_newest is None
|
|
or course_session.generation > generation_newest
|
|
):
|
|
newest = course_session
|
|
|
|
# competence
|
|
_, success_total, fail_total = competences(
|
|
course_slug=str(course.slug),
|
|
course_session_selection_ids=course_session_for_user,
|
|
user_selection_ids=[str(user.id)],
|
|
)
|
|
|
|
# assignment
|
|
evaluation_results = AssignmentCompletion.objects.filter(
|
|
completion_status=AssignmentCompletionStatus.EVALUATION_SUBMITTED.value,
|
|
assignment_user=user,
|
|
course_session__course=course,
|
|
).values(
|
|
"evaluation_max_points", "evaluation_points", "evaluation_points_deducted"
|
|
)
|
|
|
|
evaluation_results = list(evaluation_results)
|
|
points_max_count = sum(
|
|
[result.get("evaluation_max_points", 0) for result in evaluation_results]
|
|
)
|
|
points_achieved_count = sum(
|
|
[
|
|
(
|
|
result.get("evaluation_points", 0)
|
|
- result.get("evaluation_points_deducted", 0)
|
|
)
|
|
for result in evaluation_results
|
|
]
|
|
)
|
|
|
|
return CourseProgressType(
|
|
_id=course_id, # noqa
|
|
course_id=course_id, # noqa
|
|
session_to_continue_id=newest.id if newest else None, # noqa
|
|
competence=ProgressDashboardCompetenceType( # noqa
|
|
_id=course_id, # noqa
|
|
total_count=success_total + fail_total, # noqa
|
|
success_count=success_total, # noqa
|
|
fail_count=fail_total, # noqa
|
|
),
|
|
assignment=ProgressDashboardAssignmentType( # noqa
|
|
_id=course_id, # noqa
|
|
total_count=len(evaluation_results), # noqa
|
|
points_max_count=int(points_max_count), # noqa
|
|
points_achieved_count=int(points_achieved_count), # noqa
|
|
),
|
|
)
|
|
|
|
|
|
def get_user_statistics_dashboards(user: User) -> Tuple[List[Dict[str, str]], Set[int]]:
|
|
course_ids = set()
|
|
dashboards = []
|
|
|
|
for group in CourseSessionGroup.objects.all():
|
|
if can_view_course_session_group_statistics(user=user, group=group):
|
|
course = group.course
|
|
course_ids.add(course)
|
|
dashboards.append(
|
|
{
|
|
"id": str(course.id),
|
|
"name": course.title,
|
|
"slug": course.slug,
|
|
"dashboard_type": DashboardType.STATISTICS_DASHBOARD,
|
|
"course_configuration": course.configuration,
|
|
}
|
|
)
|
|
|
|
return dashboards, course_ids
|
|
|
|
|
|
def get_learning_mentor_dashboards(
|
|
user: User, exclude_course_ids: Set[int]
|
|
) -> Tuple[List[Dict[str, str]], Set[int]]:
|
|
learning_mentor_relation_qs = AgentParticipantRelation.objects.filter(
|
|
agent=user, role=AgentParticipantRoleType.LEARNING_MENTOR.value
|
|
).exclude(participant__course_session__course__id__in=exclude_course_ids)
|
|
|
|
dashboards = []
|
|
course_ids = set()
|
|
|
|
for rel in learning_mentor_relation_qs:
|
|
course = rel.participant.course_session.course
|
|
if course.id in UK_COURSE_IDS:
|
|
dashboard_type = DashboardType.PRAXISBILDNER_DASHBOARD
|
|
else:
|
|
dashboard_type = DashboardType.MENTOR_DASHBOARD
|
|
|
|
if course.id not in course_ids:
|
|
course_ids.add(course.id)
|
|
dashboards.append(
|
|
{
|
|
"id": str(course.id),
|
|
"name": course.title,
|
|
"slug": course.slug,
|
|
"dashboard_type": dashboard_type,
|
|
"course_configuration": course.configuration,
|
|
}
|
|
)
|
|
|
|
return dashboards, course_ids
|
|
|
|
|
|
def get_user_course_session_dashboards(
|
|
user: User, exclude_course_ids: Set[int]
|
|
) -> Tuple[List[Dict[str, str]], Set[int]]:
|
|
"""
|
|
Edge case: what do we show to users with access to multiple
|
|
sessions of a course, but with varying permissions?
|
|
-> We just show the simple list dashboard for now.
|
|
"""
|
|
dashboards = []
|
|
course_ids = set()
|
|
|
|
course_sessions = CourseSession.objects.exclude(course__in=exclude_course_ids)
|
|
roles_by_course: Dict[Course, Set[DashboardType]] = {}
|
|
|
|
for course_session in course_sessions:
|
|
if can_view_course_session(user=user, course_session=course_session):
|
|
role = CourseSessionUser.objects.get(
|
|
course_session=course_session, user=user
|
|
).role
|
|
roles_by_course.setdefault(course_session.course, set())
|
|
roles_by_course[course_session.course].add(role)
|
|
|
|
for course, roles in roles_by_course.items():
|
|
resolved_dashboard_type = None
|
|
|
|
if len(roles) == 1:
|
|
course_role = roles.pop()
|
|
if course_role == CourseSessionUser.Role.EXPERT:
|
|
resolved_dashboard_type = DashboardType.SIMPLE_DASHBOARD
|
|
elif course_role == CourseSessionUser.Role.MEMBER:
|
|
resolved_dashboard_type = DashboardType.PROGRESS_DASHBOARD
|
|
|
|
else:
|
|
# fallback: just go with simple list dashboard
|
|
resolved_dashboard_type = DashboardType.SIMPLE_DASHBOARD
|
|
|
|
course_ids.add(course.id)
|
|
|
|
dashboards.append(
|
|
{
|
|
"id": str(course.id),
|
|
"name": course.title,
|
|
"slug": course.slug,
|
|
"dashboard_type": resolved_dashboard_type,
|
|
"course_configuration": course.configuration,
|
|
}
|
|
)
|
|
|
|
return dashboards, course_ids
|