wip: session group statistics w/ permission
This commit is contained in:
parent
c7920430ca
commit
ca44a913c9
|
|
@ -10,7 +10,7 @@ from vbv_lernwelt.assignment.models import Assignment, AssignmentCompletionStatu
|
|||
from vbv_lernwelt.assignment.services import update_assignment_completion
|
||||
from vbv_lernwelt.core.models import User
|
||||
from vbv_lernwelt.course.models import CourseSession, CourseSessionUser
|
||||
from vbv_lernwelt.course.permissions import has_course_access, is_course_session_expert
|
||||
from vbv_lernwelt.iam.permissions import has_course_access, is_course_session_expert
|
||||
|
||||
logger = structlog.get_logger(__name__)
|
||||
|
||||
|
|
|
|||
|
|
@ -7,7 +7,7 @@ from vbv_lernwelt.assignment.models import Assignment, AssignmentCompletion
|
|||
from vbv_lernwelt.core.graphql.types import JSONStreamField
|
||||
from vbv_lernwelt.course.graphql.interfaces import CoursePageInterface
|
||||
from vbv_lernwelt.course.models import CourseSession
|
||||
from vbv_lernwelt.course.permissions import has_course_access, is_course_session_expert
|
||||
from vbv_lernwelt.iam.permissions import has_course_access, is_course_session_expert
|
||||
from vbv_lernwelt.learnpath.graphql.types import LearningContentInterface
|
||||
|
||||
|
||||
|
|
|
|||
|
|
@ -4,7 +4,7 @@ from rest_framework.exceptions import PermissionDenied
|
|||
from rest_framework.response import Response
|
||||
|
||||
from vbv_lernwelt.assignment.models import AssignmentCompletion
|
||||
from vbv_lernwelt.course.permissions import is_course_session_expert
|
||||
from vbv_lernwelt.iam.permissions import is_course_session_expert
|
||||
|
||||
logger = structlog.get_logger(__name__)
|
||||
|
||||
|
|
|
|||
|
|
@ -4,7 +4,7 @@ from graphql import GraphQLError
|
|||
|
||||
from vbv_lernwelt.course.graphql.types import CourseObjectType, CourseSessionObjectType
|
||||
from vbv_lernwelt.course.models import Course, CourseSession
|
||||
from vbv_lernwelt.course.permissions import has_course_access
|
||||
from vbv_lernwelt.iam.permissions import has_course_access
|
||||
from vbv_lernwelt.learnpath.graphql.types import (
|
||||
LearningContentAssignmentObjectType,
|
||||
LearningContentAttendanceCourseObjectType,
|
||||
|
|
|
|||
|
|
@ -16,7 +16,6 @@ from vbv_lernwelt.course.models import (
|
|||
CourseSession,
|
||||
CourseSessionUser,
|
||||
)
|
||||
from vbv_lernwelt.course.permissions import has_course_access
|
||||
from vbv_lernwelt.course_session.graphql.types import (
|
||||
CourseSessionAssignmentObjectType,
|
||||
CourseSessionAttendanceCourseObjectType,
|
||||
|
|
@ -27,6 +26,7 @@ from vbv_lernwelt.course_session.models import (
|
|||
CourseSessionAttendanceCourse,
|
||||
CourseSessionEdoniqTest,
|
||||
)
|
||||
from vbv_lernwelt.iam.permissions import has_course_access
|
||||
from vbv_lernwelt.learnpath.graphql.types import LearningPathObjectType
|
||||
|
||||
logger = structlog.get_logger(__name__)
|
||||
|
|
|
|||
|
|
@ -10,14 +10,6 @@ from vbv_lernwelt.course.models import (
|
|||
CircleDocument,
|
||||
CourseCompletion,
|
||||
CourseSession,
|
||||
CourseSessionUser,
|
||||
)
|
||||
from vbv_lernwelt.course.permissions import (
|
||||
course_sessions_for_user_qs,
|
||||
has_course_access,
|
||||
has_course_access_by_page_request,
|
||||
is_circle_expert,
|
||||
is_course_session_expert,
|
||||
)
|
||||
from vbv_lernwelt.course.serializers import (
|
||||
CourseCompletionSerializer,
|
||||
|
|
@ -28,6 +20,13 @@ from vbv_lernwelt.course.serializers import (
|
|||
from vbv_lernwelt.course.services import mark_course_completion
|
||||
from vbv_lernwelt.files.models import UploadFile
|
||||
from vbv_lernwelt.files.services import FileDirectUploadService
|
||||
from vbv_lernwelt.iam.permissions import (
|
||||
has_course_access_by_page_request,
|
||||
has_course_access,
|
||||
is_course_session_expert,
|
||||
course_sessions_for_user_qs,
|
||||
is_circle_expert,
|
||||
)
|
||||
|
||||
logger = structlog.get_logger(__name__)
|
||||
|
||||
|
|
|
|||
|
|
@ -2,7 +2,6 @@ import graphene
|
|||
import structlog
|
||||
from rest_framework.exceptions import PermissionDenied
|
||||
|
||||
from vbv_lernwelt.course.permissions import has_course_access
|
||||
from vbv_lernwelt.course_session.graphql.types import (
|
||||
CourseSessionAttendanceCourseObjectType,
|
||||
)
|
||||
|
|
@ -11,6 +10,7 @@ from vbv_lernwelt.course_session.services.attendance import (
|
|||
AttendanceUserStatus,
|
||||
update_attendance_list,
|
||||
)
|
||||
from vbv_lernwelt.iam.permissions import has_course_access
|
||||
|
||||
logger = structlog.get_logger(__name__)
|
||||
|
||||
|
|
|
|||
|
|
@ -2,11 +2,11 @@ import graphene
|
|||
from rest_framework.exceptions import PermissionDenied
|
||||
|
||||
from vbv_lernwelt.course.models import CourseSession
|
||||
from vbv_lernwelt.course.permissions import has_course_access, is_course_session_expert
|
||||
from vbv_lernwelt.course_session.graphql.types import (
|
||||
CourseSessionAttendanceCourseObjectType,
|
||||
)
|
||||
from vbv_lernwelt.course_session.models import CourseSessionAttendanceCourse
|
||||
from vbv_lernwelt.iam.permissions import has_course_access, is_course_session_expert
|
||||
|
||||
|
||||
class CourseSessionQuery(object):
|
||||
|
|
|
|||
|
|
@ -1,7 +1,6 @@
|
|||
import graphene
|
||||
from graphene_django import DjangoObjectType
|
||||
|
||||
from vbv_lernwelt.course.permissions import is_course_session_expert
|
||||
from vbv_lernwelt.course_session.models import (
|
||||
CourseSessionAssignment,
|
||||
CourseSessionAttendanceCourse,
|
||||
|
|
@ -9,6 +8,7 @@ from vbv_lernwelt.course_session.models import (
|
|||
)
|
||||
from vbv_lernwelt.course_session.services.attendance import AttendanceUserStatus
|
||||
from vbv_lernwelt.duedate.graphql.types import DueDateObjectType
|
||||
from vbv_lernwelt.iam.permissions import is_course_session_expert
|
||||
from vbv_lernwelt.learnpath.graphql.types import (
|
||||
LearningContentAssignmentObjectType,
|
||||
LearningContentAttendanceCourseObjectType,
|
||||
|
|
|
|||
|
|
@ -3,8 +3,8 @@ from rest_framework.exceptions import PermissionDenied
|
|||
from rest_framework.response import Response
|
||||
|
||||
from vbv_lernwelt.course.models import CircleDocument
|
||||
from vbv_lernwelt.course.permissions import has_course_session_access
|
||||
from vbv_lernwelt.course.serializers import CircleDocumentSerializer
|
||||
from vbv_lernwelt.iam.permissions import has_course_session_access
|
||||
|
||||
|
||||
@api_view(["GET"])
|
||||
|
|
|
|||
|
|
@ -1,22 +1,64 @@
|
|||
import graphene
|
||||
|
||||
from vbv_lernwelt.course.models import Course
|
||||
from vbv_lernwelt.dashboard.graphql.types.dashboard import CourseStatisticsType
|
||||
from vbv_lernwelt.course.models import CourseSession, Course
|
||||
from vbv_lernwelt.course_session_group.models import CourseSessionGroup
|
||||
from vbv_lernwelt.dashboard.graphql.types.dashboard import CourseStatisticsType, DashboardConfigType
|
||||
from vbv_lernwelt.iam.permissions import can_view_course_session_group_statistics, can_view_course_session
|
||||
|
||||
|
||||
class DashboardQuery(graphene.ObjectType):
|
||||
course_statistics = graphene.List(
|
||||
CourseStatisticsType, course_id=graphene.ID(required=True)
|
||||
course_statistics = graphene.Field(CourseStatisticsType, course_id=graphene.ID(required=True))
|
||||
|
||||
dashboard_config = graphene.List(
|
||||
DashboardConfigType
|
||||
)
|
||||
|
||||
def resolve_course_statistics(root, info, course_id: str):
|
||||
query = Course.objects.filter(
|
||||
id=course_id
|
||||
)
|
||||
def resolve_course_statistics(root, info, course_id: str): # noqa
|
||||
user = info.context.user
|
||||
course = Course.objects.get(id=course_id)
|
||||
|
||||
courses = query.distinct()
|
||||
course_session_ids = set()
|
||||
|
||||
return [
|
||||
CourseStatisticsType(course_id=course.id, course_title=course.title) # noqa
|
||||
for course in courses
|
||||
]
|
||||
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))
|
||||
|
||||
if not course_session_ids:
|
||||
return None
|
||||
|
||||
|
||||
return CourseStatisticsType(course_id=course.id, course_title=course.title, # noqa
|
||||
course_session_selection_ids=list(course_session_ids)) # noqa
|
||||
|
||||
|
||||
|
||||
def resolve_dashboard_config(root, info): # noqa
|
||||
user = info.context.user
|
||||
|
||||
course_index = set()
|
||||
dashboards = []
|
||||
|
||||
for group in CourseSessionGroup.objects.all():
|
||||
if can_view_course_session_group_statistics(user=user, group=group):
|
||||
course = group.course
|
||||
course_index.add(course)
|
||||
dashboards.append(
|
||||
{
|
||||
"id": str(course.id),
|
||||
"title": course.title,
|
||||
"dashboard_type": "StatisticsDashboard",
|
||||
}
|
||||
)
|
||||
|
||||
for course_session in CourseSession.objects.exclude(course__in=course_index):
|
||||
if can_view_course_session(user=user, course_session=course_session):
|
||||
course = course_session.course
|
||||
dashboards.append(
|
||||
{
|
||||
"id": str(course.id),
|
||||
"title": course.title,
|
||||
"dashboard_type": "SimpleDashboard",
|
||||
}
|
||||
)
|
||||
|
||||
return dashboards
|
||||
|
|
|
|||
|
|
@ -124,9 +124,11 @@ def create_record(
|
|||
)
|
||||
|
||||
|
||||
def assignments(course_id) -> Assignments:
|
||||
def assignments(
|
||||
course_session_selection_ids: graphene.List(graphene.ID),
|
||||
) -> Assignments:
|
||||
course_sessions = CourseSession.objects.filter(
|
||||
course_id=course_id,
|
||||
id_in=course_session_selection_ids,
|
||||
)
|
||||
|
||||
records: List[AssignmentRecord] = []
|
||||
|
|
|
|||
|
|
@ -30,9 +30,11 @@ class AttendanceDayPresences(graphene.ObjectType):
|
|||
summary = graphene.Field(AttendanceSummary)
|
||||
|
||||
|
||||
def attendance_day_presences(course_id: graphene.String()) -> AttendanceDayPresences:
|
||||
def attendance_day_presences(
|
||||
course_session_selection_ids: graphene.List(graphene.ID),
|
||||
) -> AttendanceDayPresences:
|
||||
completed = CourseSessionAttendanceCourse.objects.filter(
|
||||
course_session__course_id=course_id,
|
||||
course_session_id__in=course_session_selection_ids,
|
||||
due_date__end__lt=timezone.now(),
|
||||
).order_by("-due_date__end")
|
||||
|
||||
|
|
|
|||
|
|
@ -21,9 +21,11 @@ class Competences(graphene.ObjectType):
|
|||
summary = graphene.Field(CompletionSummary)
|
||||
|
||||
|
||||
def competences(course_id: graphene.String()) -> Competences:
|
||||
def competences(
|
||||
course_session_selection_ids: graphene.List(graphene.ID),
|
||||
) -> Competences:
|
||||
completions = CourseCompletion.objects.filter(
|
||||
course_session__course_id=course_id,
|
||||
course_session_id__in=course_session_selection_ids,
|
||||
page_type="competence.PerformanceCriteria",
|
||||
)
|
||||
|
||||
|
|
|
|||
|
|
@ -31,9 +31,16 @@ class CourseSessionProperties(graphene.ObjectType):
|
|||
circles = graphene.List(CircleData)
|
||||
|
||||
|
||||
class DashboardConfigType(graphene.ObjectType):
|
||||
id = graphene.ID()
|
||||
title = graphene.String()
|
||||
dashboard_type = graphene.String()
|
||||
|
||||
|
||||
class CourseStatisticsType(graphene.ObjectType):
|
||||
course_id = graphene.String()
|
||||
course_id = graphene.ID()
|
||||
course_title = graphene.String()
|
||||
course_session_selection_ids = graphene.List(graphene.ID)
|
||||
course_session_properties = graphene.Field(CourseSessionProperties)
|
||||
attendance_day_presences = graphene.Field(AttendanceDayPresences)
|
||||
feedback_responses = graphene.Field(FeedbackResponses)
|
||||
|
|
@ -41,16 +48,16 @@ class CourseStatisticsType(graphene.ObjectType):
|
|||
competences = graphene.Field(Competences)
|
||||
|
||||
def resolve_attendance_day_presences(root, info) -> AttendanceDayPresences:
|
||||
return attendance_day_presences(root.course_id)
|
||||
return attendance_day_presences(root.course_session_selection_ids)
|
||||
|
||||
def resolve_feedback_responses(root, info) -> FeedbackResponses:
|
||||
return feedback_responses(root.course_id)
|
||||
return feedback_responses(root.course_session_selection_ids)
|
||||
|
||||
def resolve_competences(root, info) -> Competences:
|
||||
return competences(root.course_id)
|
||||
return competences(root.course_session_selection_ids)
|
||||
|
||||
def resolve_assignments(root, info) -> Assignments:
|
||||
return assignments(root.course_id)
|
||||
return assignments(root.course_session_selection_ids)
|
||||
|
||||
def resolve_course_session_properties(root, info):
|
||||
course_session_data = []
|
||||
|
|
|
|||
|
|
@ -26,10 +26,12 @@ class FeedbackResponses(graphene.ObjectType):
|
|||
summary = graphene.Field(FeedbackSummary)
|
||||
|
||||
|
||||
def feedback_responses(course_id: graphene.String()) -> FeedbackResponses:
|
||||
def feedback_responses(
|
||||
course_session_selection_ids: graphene.List(graphene.ID),
|
||||
) -> FeedbackResponses:
|
||||
# Get all course sessions for this user in the given course
|
||||
course_sessions = CourseSession.objects.filter(
|
||||
course_id=course_id,
|
||||
id_in=course_session_selection_ids,
|
||||
)
|
||||
|
||||
circle_feedbacks = []
|
||||
|
|
|
|||
|
|
@ -1,15 +1,70 @@
|
|||
from graphene_django.utils import GraphQLTestCase
|
||||
|
||||
from vbv_lernwelt.course.models import CourseSessionUser
|
||||
from vbv_lernwelt.dashboard.tests.graphql.utils import (
|
||||
create_course,
|
||||
create_course_session,
|
||||
create_user,
|
||||
add_course_session_user,
|
||||
create_course_session_group,
|
||||
)
|
||||
|
||||
|
||||
class DashboardTestCase(GraphQLTestCase):
|
||||
GRAPHQL_URL = "/server/graphql/"
|
||||
|
||||
def test_dashboard_config(self):
|
||||
# GIVEN
|
||||
course_1, _ = create_course("Test Course 1")
|
||||
course_2, _ = create_course("Test Course 2")
|
||||
|
||||
cs_1 = create_course_session(course=course_1, title="Test Course 1 Session")
|
||||
cs_2 = create_course_session(course=course_2, title="Test Course 2 Session")
|
||||
|
||||
# Member of course 1 (via cs_1)
|
||||
# Supervisor of course 2 (via cs_2)
|
||||
supervisor = create_user("supervisor")
|
||||
|
||||
add_course_session_user(
|
||||
course_session=cs_1, user=supervisor, role=CourseSessionUser.Role.MEMBER
|
||||
)
|
||||
|
||||
create_course_session_group(course_session=cs_2, user=supervisor)
|
||||
|
||||
self.client.force_login(supervisor)
|
||||
|
||||
# WHEN
|
||||
query = """query {
|
||||
dashboard_config {
|
||||
id
|
||||
title
|
||||
dashboard_type
|
||||
}
|
||||
}
|
||||
"""
|
||||
|
||||
response = self.query(query)
|
||||
|
||||
# THEN
|
||||
self.assertResponseNoErrors(response)
|
||||
|
||||
dashboard_config = response.json()["data"]["dashboard_config"]
|
||||
self.assertEqual(len(dashboard_config), 2)
|
||||
|
||||
course_1_config = find_dashboard_config_by_course_id(
|
||||
dashboard_config, course_1.id
|
||||
)
|
||||
self.assertIsNotNone(course_1_config)
|
||||
self.assertEqual(course_1_config["title"], course_1.title)
|
||||
self.assertEqual(course_1_config["dashboard_type"], "SimpleDashboard")
|
||||
|
||||
course_2_config = find_dashboard_config_by_course_id(
|
||||
dashboard_config, course_2.id
|
||||
)
|
||||
self.assertIsNotNone(course_2_config)
|
||||
self.assertEqual(course_2_config["title"], course_2.title)
|
||||
self.assertEqual(course_2_config["dashboard_type"], "StatisticsDashboard")
|
||||
|
||||
def test_course_statistics_id(self):
|
||||
# GIVEN
|
||||
|
||||
|
|
@ -41,3 +96,9 @@ class DashboardTestCase(GraphQLTestCase):
|
|||
|
||||
self.assertEqual(len(course_statistics), 1)
|
||||
self.assertEqual(course_statistics[0]["course_id"], str(course_2.id))
|
||||
|
||||
|
||||
def find_dashboard_config_by_course_id(dashboard_configs, course_id):
|
||||
return next(
|
||||
(config for config in dashboard_configs if config["id"] == str(course_id)), None
|
||||
)
|
||||
|
|
|
|||
|
|
@ -36,6 +36,7 @@ from vbv_lernwelt.course_session.models import (
|
|||
CourseSessionAttendanceCourse,
|
||||
CourseSessionEdoniqTest,
|
||||
)
|
||||
from vbv_lernwelt.course_session_group.models import CourseSessionGroup
|
||||
from vbv_lernwelt.duedate.models import DueDate
|
||||
from vbv_lernwelt.learnpath.models import (
|
||||
Circle,
|
||||
|
|
@ -99,6 +100,19 @@ def add_course_session_user(
|
|||
)
|
||||
|
||||
|
||||
def create_course_session_group(
|
||||
course_session: CourseSession, user: User
|
||||
) -> CourseSessionGroup:
|
||||
g = CourseSessionGroup.objects.create(
|
||||
course=course_session.course,
|
||||
)
|
||||
|
||||
g.course_session.add(course_session)
|
||||
g.supervisor.add(user)
|
||||
|
||||
return g
|
||||
|
||||
|
||||
def create_circle(
|
||||
title: str, course_page: CoursePage, learning_path: LearningPath | None = None
|
||||
) -> Tuple[Circle, LearningPath]:
|
||||
|
|
|
|||
|
|
@ -14,8 +14,8 @@ from rest_framework.decorators import api_view
|
|||
from vbv_lernwelt.core.models import User
|
||||
from vbv_lernwelt.course.consts import COURSE_UK, COURSE_UK_FR, COURSE_UK_IT
|
||||
from vbv_lernwelt.course.models import CourseSessionUser
|
||||
from vbv_lernwelt.course.permissions import has_course_access_by_page_request
|
||||
from vbv_lernwelt.edoniq_test.edoniq_sso import create_token
|
||||
from vbv_lernwelt.iam.permissions import has_course_access_by_page_request
|
||||
from vbv_lernwelt.learnpath.models import LearningContentEdoniqTest
|
||||
|
||||
logger = structlog.get_logger(__name__)
|
||||
|
|
|
|||
|
|
@ -4,12 +4,12 @@ from graphene.types.generic import GenericScalar
|
|||
from graphene_django.types import ErrorType
|
||||
|
||||
from vbv_lernwelt.course.models import CourseSession
|
||||
from vbv_lernwelt.course.permissions import has_course_session_access
|
||||
from vbv_lernwelt.feedback.graphql.types import (
|
||||
FeedbackResponseObjectType as FeedbackResponseType,
|
||||
)
|
||||
from vbv_lernwelt.feedback.serializers import CourseFeedbackSerializer
|
||||
from vbv_lernwelt.feedback.services import update_feedback_response
|
||||
from vbv_lernwelt.iam.permissions import has_course_session_access
|
||||
from vbv_lernwelt.learnpath.models import LearningContentFeedback
|
||||
|
||||
logger = structlog.get_logger(__name__)
|
||||
|
|
|
|||
|
|
@ -5,9 +5,9 @@ from rest_framework.decorators import api_view
|
|||
from rest_framework.exceptions import PermissionDenied
|
||||
from rest_framework.response import Response
|
||||
|
||||
from vbv_lernwelt.course.permissions import is_course_session_expert
|
||||
from vbv_lernwelt.feedback.models import FeedbackResponse
|
||||
from vbv_lernwelt.feedback.utils import feedback_users
|
||||
from vbv_lernwelt.iam.permissions import is_course_session_expert
|
||||
|
||||
logger = structlog.get_logger(__name__)
|
||||
|
||||
|
|
|
|||
|
|
@ -1,4 +1,6 @@
|
|||
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.learnpath.models import LearningSequence
|
||||
|
||||
|
||||
|
|
@ -10,38 +12,29 @@ def has_course_access(user, course_id):
|
|||
if user.is_superuser:
|
||||
return True
|
||||
|
||||
if CourseSessionUser.objects.filter(
|
||||
return CourseSessionUser.objects.filter(
|
||||
course_session__course_id=course_id, user=user
|
||||
).exists():
|
||||
return True
|
||||
|
||||
return False
|
||||
).exists()
|
||||
|
||||
|
||||
def has_course_session_access(user, course_session_id: int):
|
||||
if user.is_superuser:
|
||||
return True
|
||||
|
||||
if CourseSessionUser.objects.filter(
|
||||
return CourseSessionUser.objects.filter(
|
||||
course_session_id=course_session_id, user=user
|
||||
).exists():
|
||||
return True
|
||||
|
||||
return False
|
||||
).exists()
|
||||
|
||||
|
||||
def is_course_session_expert(user, course_session_id: int):
|
||||
if user.is_superuser:
|
||||
return True
|
||||
|
||||
if CourseSessionUser.objects.filter(
|
||||
return CourseSessionUser.objects.filter(
|
||||
course_session_id=course_session_id,
|
||||
user=user,
|
||||
role=CourseSessionUser.Role.EXPERT,
|
||||
).exists():
|
||||
return True
|
||||
|
||||
return False
|
||||
).exists()
|
||||
|
||||
|
||||
def course_sessions_for_user_qs(user):
|
||||
|
|
@ -64,12 +57,33 @@ def is_circle_expert(user, course_session_id: int, learning_sequence_id: int) ->
|
|||
|
||||
circle_id = learning_sequence.get_parent().circle.id
|
||||
|
||||
if CourseSessionUser.objects.filter(
|
||||
return CourseSessionUser.objects.filter(
|
||||
course_session_id=course_session_id,
|
||||
user=user,
|
||||
role=CourseSessionUser.Role.EXPERT,
|
||||
expert__id=circle_id,
|
||||
).exists()
|
||||
|
||||
|
||||
def can_view_course_session_group_statistics(
|
||||
user: User, group: CourseSessionGroup
|
||||
) -> bool:
|
||||
if user.is_superuser:
|
||||
return True
|
||||
|
||||
return user in group.supervisor.all()
|
||||
|
||||
|
||||
def can_view_course_session(user: User, course_session: CourseSession) -> bool:
|
||||
if user.is_superuser:
|
||||
return True
|
||||
|
||||
if CourseSessionGroup.objects.filter(
|
||||
course_session=course_session, supervisor=user
|
||||
).exists():
|
||||
return True
|
||||
|
||||
return False
|
||||
return CourseSessionUser.objects.filter(
|
||||
course_session=course_session,
|
||||
user=user,
|
||||
).exists()
|
||||
Loading…
Reference in New Issue