def get(self, request): """GET /api/team/v0/teams/""" result_filter = {'is_active': True} if 'course_id' in request.QUERY_PARAMS: course_id_string = request.QUERY_PARAMS['course_id'] try: course_key = CourseKey.from_string(course_id_string) # Ensure the course exists if not modulestore().has_course(course_key): return Response(status=status.HTTP_404_NOT_FOUND) result_filter.update({'course_id': course_key}) except InvalidKeyError: error = build_api_error( ugettext_noop( "The supplied course id {course_id} is not valid."), course_id=course_id_string, ) return Response(error, status=status.HTTP_400_BAD_REQUEST) if not has_team_api_access(request.user, course_key): return Response(status=status.HTTP_403_FORBIDDEN) else: return Response(build_api_error( ugettext_noop("course_id must be provided")), status=status.HTTP_400_BAD_REQUEST) if 'topic_id' in request.QUERY_PARAMS: result_filter.update( {'topic_id': request.QUERY_PARAMS['topic_id']}) if 'include_inactive' in request.QUERY_PARAMS and request.QUERY_PARAMS[ 'include_inactive'].lower() == 'true': del result_filter['is_active'] if 'text_search' in request.QUERY_PARAMS: return Response(build_api_error( ugettext_noop("text_search is not yet supported.")), status=status.HTTP_400_BAD_REQUEST) queryset = CourseTeam.objects.filter(**result_filter) order_by_input = request.QUERY_PARAMS.get('order_by', 'name') if order_by_input == 'name': queryset = queryset.extra(select={'lower_name': "lower(name)"}) order_by_field = 'lower_name' elif order_by_input == 'open_slots': queryset = queryset.annotate(team_size=Count('users')) order_by_field = 'team_size' elif order_by_input == 'last_activity': return Response(build_api_error( ugettext_noop("last_activity is not yet supported")), status=status.HTTP_400_BAD_REQUEST) queryset = queryset.order_by(order_by_field) if not queryset: return Response(status=status.HTTP_404_NOT_FOUND) page = self.paginate_queryset(queryset) serializer = self.get_pagination_serializer(page) return Response(serializer.data) # pylint: disable=maybe-no-member
def post(self, request): """POST /api/team/v0/team_membership""" field_errors = {} if 'username' not in request.DATA: field_errors['username'] = build_api_error( ugettext_noop("Username is required.")) if 'team_id' not in request.DATA: field_errors['team_id'] = build_api_error( ugettext_noop("Team id is required.")) if field_errors: return Response({ 'field_errors': field_errors, }, status=status.HTTP_400_BAD_REQUEST) try: team = CourseTeam.objects.get(team_id=request.DATA['team_id']) except CourseTeam.DoesNotExist: return Response(status=status.HTTP_404_NOT_FOUND) username = request.DATA['username'] if not has_team_api_access( request.user, team.course_id, access_username=username): return Response(status=status.HTTP_404_NOT_FOUND) try: user = User.objects.get(username=username) except User.DoesNotExist: return Response(status=status.HTTP_404_NOT_FOUND) course_module = modulestore().get_course(team.course_id) if course_module.teams_max_size is not None and team.users.count( ) >= course_module.teams_max_size: return Response(build_api_error( ugettext_noop("This team is already full.")), status=status.HTTP_400_BAD_REQUEST) try: membership = team.add_user(user) except AlreadyOnTeamInCourse: return Response(build_api_error(ugettext_noop( "The user {username} is already a member of a team in this course." ), username=username), status=status.HTTP_400_BAD_REQUEST) except NotEnrolledInCourseForTeam: return Response(build_api_error(ugettext_noop( "The user {username} is not enrolled in the course associated with this team." ), username=username), status=status.HTTP_400_BAD_REQUEST) serializer = self.get_serializer(instance=membership) return Response(serializer.data)
def post(self, request): """POST /api/team/v0/team_membership""" field_errors = {} if 'username' not in request.DATA: field_errors['username'] = build_api_error(ugettext_noop("Username is required.")) if 'team_id' not in request.DATA: field_errors['team_id'] = build_api_error(ugettext_noop("Team id is required.")) if field_errors: return Response({ 'field_errors': field_errors, }, status=status.HTTP_400_BAD_REQUEST) try: team = CourseTeam.objects.get(team_id=request.DATA['team_id']) except CourseTeam.DoesNotExist: return Response(status=status.HTTP_404_NOT_FOUND) username = request.DATA['username'] if not has_team_api_access(request.user, team.course_id, access_username=username): return Response(status=status.HTTP_404_NOT_FOUND) try: user = User.objects.get(username=username) except User.DoesNotExist: return Response(status=status.HTTP_404_NOT_FOUND) course_module = modulestore().get_course(team.course_id) if course_module.teams_max_size is not None and team.users.count() >= course_module.teams_max_size: return Response( build_api_error(ugettext_noop("This team is already full.")), status=status.HTTP_400_BAD_REQUEST ) try: membership = team.add_user(user) except AlreadyOnTeamInCourse: return Response( build_api_error( ugettext_noop("The user {username} is already a member of a team in this course."), username=username ), status=status.HTTP_400_BAD_REQUEST ) except NotEnrolledInCourseForTeam: return Response( build_api_error( ugettext_noop("The user {username} is not enrolled in the course associated with this team."), username=username ), status=status.HTTP_400_BAD_REQUEST ) serializer = self.get_serializer(instance=membership) return Response(serializer.data)
def get(self, request): """GET /api/team/v0/teams/""" result_filter = {"is_active": True} if "course_id" in request.QUERY_PARAMS: course_id_string = request.QUERY_PARAMS["course_id"] try: course_key = CourseKey.from_string(course_id_string) # Ensure the course exists if not modulestore().has_course(course_key): return Response(status=status.HTTP_404_NOT_FOUND) result_filter.update({"course_id": course_key}) except InvalidKeyError: error = build_api_error( ugettext_noop("The supplied course id {course_id} is not valid."), course_id=course_id_string ) return Response(error, status=status.HTTP_400_BAD_REQUEST) if not has_team_api_access(request.user, course_key): return Response(status=status.HTTP_403_FORBIDDEN) else: return Response( build_api_error(ugettext_noop("course_id must be provided")), status=status.HTTP_400_BAD_REQUEST ) if "topic_id" in request.QUERY_PARAMS: result_filter.update({"topic_id": request.QUERY_PARAMS["topic_id"]}) if "include_inactive" in request.QUERY_PARAMS and request.QUERY_PARAMS["include_inactive"].lower() == "true": del result_filter["is_active"] if "text_search" in request.QUERY_PARAMS: return Response( build_api_error(ugettext_noop("text_search is not yet supported.")), status=status.HTTP_400_BAD_REQUEST ) queryset = CourseTeam.objects.filter(**result_filter) order_by_input = request.QUERY_PARAMS.get("order_by", "name") if order_by_input == "name": queryset = queryset.extra(select={"lower_name": "lower(name)"}) order_by_field = "lower_name" elif order_by_input == "open_slots": queryset = queryset.annotate(team_size=Count("users")) order_by_field = "team_size" elif order_by_input == "last_activity": return Response( build_api_error(ugettext_noop("last_activity is not yet supported")), status=status.HTTP_400_BAD_REQUEST ) queryset = queryset.order_by(order_by_field) if not queryset: return Response(status=status.HTTP_404_NOT_FOUND) page = self.paginate_queryset(queryset) serializer = self.get_pagination_serializer(page) return Response(serializer.data) # pylint: disable=maybe-no-member
def post(self, request): """POST /api/team/v0/teams/""" field_errors = {} course_key = None course_id = request.DATA.get('course_id') try: course_key = CourseKey.from_string(course_id) # Ensure the course exists if not modulestore().has_course(course_key): return Response(status=status.HTTP_404_NOT_FOUND) except InvalidKeyError: field_errors['course_id'] = build_api_error(ugettext_noop( 'The supplied course_id {course_id} is not valid.'), course_id=course_id) return Response({ 'field_errors': field_errors, }, status=status.HTTP_400_BAD_REQUEST) # Course and global staff, as well as discussion "privileged" users, will not automatically # be added to a team when they create it. They are allowed to create multiple teams. team_administrator = (has_access(request.user, 'staff', course_key) or has_discussion_privileges( request.user, course_key)) if not team_administrator and CourseTeamMembership.user_in_team_for_course( request.user, course_key): error_message = build_api_error( ugettext_noop('You are already in a team in this course.'), course_id=course_id) return Response(error_message, status=status.HTTP_400_BAD_REQUEST) if course_key and not has_team_api_access(request.user, course_key): return Response(status=status.HTTP_403_FORBIDDEN) data = request.DATA.copy() data['course_id'] = course_key serializer = CourseTeamCreationSerializer(data=data) add_serializer_errors(serializer, data, field_errors) if field_errors: return Response({ 'field_errors': field_errors, }, status=status.HTTP_400_BAD_REQUEST) else: team = serializer.save() if not team_administrator: # Add the creating user to the team. team.add_user(request.user) return Response(CourseTeamSerializer(team).data)
def post(self, request): """POST /api/team/v0/teams/""" field_errors = {} course_key = None course_id = request.DATA.get("course_id") try: course_key = CourseKey.from_string(course_id) # Ensure the course exists if not modulestore().has_course(course_key): return Response(status=status.HTTP_404_NOT_FOUND) except InvalidKeyError: field_errors["course_id"] = build_api_error( ugettext_noop("The supplied course_id {course_id} is not valid."), course_id=course_id ) return Response({"field_errors": field_errors}, status=status.HTTP_400_BAD_REQUEST) # Course and global staff, as well as discussion "privileged" users, will not automatically # be added to a team when they create it. They are allowed to create multiple teams. team_administrator = has_access(request.user, "staff", course_key) or has_discussion_privileges( request.user, course_key ) if not team_administrator and CourseTeamMembership.user_in_team_for_course(request.user, course_key): error_message = build_api_error( ugettext_noop("You are already in a team in this course."), course_id=course_id ) return Response(error_message, status=status.HTTP_400_BAD_REQUEST) if course_key and not has_team_api_access(request.user, course_key): return Response(status=status.HTTP_403_FORBIDDEN) data = request.DATA.copy() data["course_id"] = course_key serializer = CourseTeamCreationSerializer(data=data) add_serializer_errors(serializer, data, field_errors) if field_errors: return Response({"field_errors": field_errors}, status=status.HTTP_400_BAD_REQUEST) else: team = serializer.save() emit_team_event("edx.team.created", course_key, {"team_id": team.team_id}) if not team_administrator: # Add the creating user to the team. team.add_user(request.user) emit_team_event( "edx.team.learner_added", course_key, {"team_id": team.team_id, "user_id": request.user.id, "add_method": "added_on_create"}, ) return Response(CourseTeamSerializer(team).data)
def post(self, request): """POST /api/team/v0/teams/""" field_errors = {} course_key = None course_id = request.DATA.get('course_id') try: course_key = CourseKey.from_string(course_id) # Ensure the course exists if not modulestore().has_course(course_key): return Response(status=status.HTTP_404_NOT_FOUND) except InvalidKeyError: field_errors['course_id'] = build_api_error( ugettext_noop('The supplied course_id {course_id} is not valid.'), course_id=course_id ) return Response({ 'field_errors': field_errors, }, status=status.HTTP_400_BAD_REQUEST) if CourseTeamMembership.user_in_team_for_course(request.user, course_key): error_message = build_api_error( ugettext_noop('You are already in a team in this course.'), course_id=course_id ) return Response({ 'error_message': error_message, }, status=status.HTTP_400_BAD_REQUEST) if course_key and not has_team_api_access(request.user, course_key): return Response(status=status.HTTP_403_FORBIDDEN) data = request.DATA.copy() data['course_id'] = course_key serializer = CourseTeamCreationSerializer(data=data) add_serializer_errors(serializer, data, field_errors) if field_errors: return Response({ 'field_errors': field_errors, }, status=status.HTTP_400_BAD_REQUEST) else: team = serializer.save() if not (has_access(request.user, 'staff', course_key) or has_discussion_privileges(request.user, course_key)): # Add the creating user to the team. team.add_user(request.user) return Response(CourseTeamSerializer(team).data)
def post(self, request): """POST /api/team/v0/team_membership""" field_errors = {} if "username" not in request.DATA: field_errors["username"] = build_api_error(ugettext_noop("Username is required.")) if "team_id" not in request.DATA: field_errors["team_id"] = build_api_error(ugettext_noop("Team id is required.")) if field_errors: return Response({"field_errors": field_errors}, status=status.HTTP_400_BAD_REQUEST) try: team = CourseTeam.objects.get(team_id=request.DATA["team_id"]) except CourseTeam.DoesNotExist: return Response(status=status.HTTP_404_NOT_FOUND) username = request.DATA["username"] if not has_team_api_access(request.user, team.course_id, access_username=username): return Response(status=status.HTTP_404_NOT_FOUND) try: user = User.objects.get(username=username) except User.DoesNotExist: return Response(status=status.HTTP_404_NOT_FOUND) try: membership = team.add_user(user) except AlreadyOnTeamInCourse: return Response( build_api_error( ugettext_noop("The user {username} is already a member of a team in this course."), username=username, ), status=status.HTTP_400_BAD_REQUEST, ) except NotEnrolledInCourseForTeam: return Response( build_api_error( ugettext_noop("The user {username} is not enrolled in the course associated with this team."), username=username, ), status=status.HTTP_400_BAD_REQUEST, ) serializer = self.get_serializer(instance=membership) return Response(serializer.data)
def post(self, request): """POST /api/team/v0/teams/""" field_errors = {} course_key = None course_id = request.DATA.get('course_id') try: course_key = CourseKey.from_string(course_id) # Ensure the course exists if not modulestore().has_course(course_key): return Response(status=status.HTTP_404_NOT_FOUND) except InvalidKeyError: field_errors['course_id'] = build_api_error(ugettext_noop( 'The supplied course_id {course_id} is not valid.'), course_id=course_id) if course_key and not has_team_api_access(request.user, course_key): return Response(status=status.HTTP_403_FORBIDDEN) data = request.DATA.copy() data['course_id'] = course_key serializer = CourseTeamCreationSerializer(data=data) add_serializer_errors(serializer, data, field_errors) if field_errors: return Response({ 'field_errors': field_errors, }, status=status.HTTP_400_BAD_REQUEST) else: team = serializer.save() return Response(CourseTeamSerializer(team).data)
def post(self, request): """POST /api/team/v0/teams/""" field_errors = {} course_key = None course_id = request.DATA.get('course_id') try: course_key = CourseKey.from_string(course_id) # Ensure the course exists if not modulestore().has_course(course_key): return Response(status=status.HTTP_404_NOT_FOUND) except InvalidKeyError: field_errors['course_id'] = build_api_error( ugettext_noop('The supplied course_id {course_id} is not valid.'), course_id=course_id ) if course_key and not has_team_api_access(request.user, course_key): return Response(status=status.HTTP_403_FORBIDDEN) data = request.DATA.copy() data['course_id'] = course_key serializer = CourseTeamCreationSerializer(data=data) add_serializer_errors(serializer, data, field_errors) if field_errors: return Response({ 'field_errors': field_errors, }, status=status.HTTP_400_BAD_REQUEST) else: team = serializer.save() return Response(CourseTeamSerializer(team).data)
def get(self, request): """GET /api/team/v0/team_membership""" specified_username_or_team = False username = None team_id = None requested_course_id = None requested_course_key = None accessible_course_ids = None if 'course_id' in request.QUERY_PARAMS: requested_course_id = request.QUERY_PARAMS['course_id'] try: requested_course_key = CourseKey.from_string(requested_course_id) except InvalidKeyError: return Response(status=status.HTTP_404_NOT_FOUND) if 'team_id' in request.QUERY_PARAMS: specified_username_or_team = True team_id = request.QUERY_PARAMS['team_id'] try: team = CourseTeam.objects.get(team_id=team_id) except CourseTeam.DoesNotExist: return Response(status=status.HTTP_404_NOT_FOUND) if requested_course_key is not None and requested_course_key != team.course_id: return Response(status=status.HTTP_400_BAD_REQUEST) if not has_team_api_access(request.user, team.course_id): return Response(status=status.HTTP_404_NOT_FOUND) if 'username' in request.QUERY_PARAMS: specified_username_or_team = True username = request.QUERY_PARAMS['username'] if not request.user.is_staff: enrolled_courses = ( CourseEnrollment.enrollments_for_user(request.user).values_list('course_id', flat=True) ) staff_courses = ( CourseAccessRole.objects.filter(user=request.user, role='staff').values_list('course_id', flat=True) ) accessible_course_ids = [item for sublist in (enrolled_courses, staff_courses) for item in sublist] if requested_course_id is not None and requested_course_id not in accessible_course_ids: return Response(status=status.HTTP_400_BAD_REQUEST) if not specified_username_or_team: return Response( build_api_error(ugettext_noop("username or team_id must be specified.")), status=status.HTTP_400_BAD_REQUEST ) course_keys = None if requested_course_key is not None: course_keys = [requested_course_key] elif accessible_course_ids is not None: course_keys = [CourseKey.from_string(course_string) for course_string in accessible_course_ids] queryset = CourseTeamMembership.get_memberships(username, course_keys, team_id) page = self.paginate_queryset(queryset) serializer = self.get_pagination_serializer(page) return Response(serializer.data) # pylint: disable=maybe-no-member
def get(self, request): """GET /api/team/v0/topics/?course_id={course_id}""" course_id_string = request.QUERY_PARAMS.get('course_id', None) if course_id_string is None: return Response( { 'field_errors': { 'course_id': build_api_error(ugettext_noop( "The supplied course id {course_id} is not valid." ), course_id=course_id_string) } }, status=status.HTTP_400_BAD_REQUEST) try: course_id = CourseKey.from_string(course_id_string) except InvalidKeyError: return Response(status=status.HTTP_404_NOT_FOUND) # Ensure the course exists course_module = modulestore().get_course(course_id) if course_module is None: # course is None if not found return Response(status=status.HTTP_404_NOT_FOUND) if not has_team_api_access(request.user, course_id): return Response(status=status.HTTP_403_FORBIDDEN) ordering = request.QUERY_PARAMS.get('order_by', 'name') if ordering == 'name': topics = get_ordered_topics(course_module, ordering) else: return Response( { 'developer_message': "unsupported order_by value {ordering}".format( ordering=ordering), # Translators: 'ordering' is a string describing a way # of ordering a list. For example, {ordering} may be # 'name', indicating that the user wants to sort the # list by lower case name. 'user_message': _(u"The ordering {ordering} is not supported").format( ordering=ordering), }, status=status.HTTP_400_BAD_REQUEST) page = self.paginate_queryset(topics) serializer = self.pagination_serializer_class(page, context={ 'course_id': course_id, 'sort_order': ordering }) return Response(serializer.data)
def get(self, request): """GET /api/team/v0/topics/?course_id={course_id}""" course_id_string = request.QUERY_PARAMS.get('course_id', None) if course_id_string is None: return Response({ 'field_errors': { 'course_id': build_api_error( ugettext_noop("The supplied course id {course_id} is not valid."), course_id=course_id_string ) } }, status=status.HTTP_400_BAD_REQUEST) try: course_id = CourseKey.from_string(course_id_string) except InvalidKeyError: return Response(status=status.HTTP_404_NOT_FOUND) # Ensure the course exists course_module = modulestore().get_course(course_id) if course_module is None: # course is None if not found return Response(status=status.HTTP_404_NOT_FOUND) if not has_team_api_access(request.user, course_id): return Response(status=status.HTTP_403_FORBIDDEN) ordering = request.QUERY_PARAMS.get('order_by', 'name') if ordering not in ['name', 'team_count']: return Response({ 'developer_message': "unsupported order_by value {ordering}".format(ordering=ordering), # Translators: 'ordering' is a string describing a way # of ordering a list. For example, {ordering} may be # 'name', indicating that the user wants to sort the # list by lower case name. 'user_message': _(u"The ordering {ordering} is not supported").format(ordering=ordering), }, status=status.HTTP_400_BAD_REQUEST) # Always sort alphabetically, as it will be used as secondary sort # in the case of "team_count". topics = get_alphabetical_topics(course_module) if ordering == 'team_count': add_team_count(topics, course_id) topics.sort(key=lambda t: t['team_count'], reverse=True) page = self.paginate_queryset(topics) # Since team_count has already been added to all the topics, use PaginatedTopicSerializer. # Even though sorting is done outside of the serializer, sort_order needs to be passed # to the serializer so that the paginated results indicate how they were sorted. serializer = PaginatedTopicSerializer(page, context={'course_id': course_id, 'sort_order': ordering}) else: page = self.paginate_queryset(topics) # Use the serializer that adds team_count in a bulk operation per page. serializer = BulkTeamCountPaginatedTopicSerializer( page, context={'course_id': course_id, 'sort_order': ordering} ) return Response(serializer.data)
def get(self, request): """GET /api/team/v0/topics/?course_id={course_id}""" course_id_string = request.QUERY_PARAMS.get("course_id", None) if course_id_string is None: return Response( { "field_errors": { "course_id": build_api_error( ugettext_noop("The supplied course id {course_id} is not valid."), course_id=course_id_string, ) } }, status=status.HTTP_400_BAD_REQUEST, ) try: course_id = CourseKey.from_string(course_id_string) except InvalidKeyError: return Response(status=status.HTTP_404_NOT_FOUND) # Ensure the course exists course_module = modulestore().get_course(course_id) if course_module is None: # course is None if not found return Response(status=status.HTTP_404_NOT_FOUND) if not has_team_api_access(request.user, course_id): return Response(status=status.HTTP_403_FORBIDDEN) topics = course_module.teams_topics ordering = request.QUERY_PARAMS.get("order_by", "name") if ordering == "name": topics = sorted(topics, key=lambda t: t["name"].lower()) else: return Response( { "developer_message": "unsupported order_by value {}".format(ordering), "user_message": _(u"The ordering {} is not supported").format(ordering), }, status=status.HTTP_400_BAD_REQUEST, ) page = self.paginate_queryset(topics) serializer = self.get_pagination_serializer(page) return Response(serializer.data) # pylint: disable=maybe-no-member
def get(self, request): """GET /api/team/v0/topics/?course_id={course_id}""" course_id_string = request.QUERY_PARAMS.get('course_id', None) if course_id_string is None: return Response( { 'field_errors': { 'course_id': build_api_error(ugettext_noop( "The supplied course id {course_id} is not valid." ), course_id=course_id_string) } }, status=status.HTTP_400_BAD_REQUEST) try: course_id = CourseKey.from_string(course_id_string) except InvalidKeyError: return Response(status=status.HTTP_404_NOT_FOUND) # Ensure the course exists course_module = modulestore().get_course(course_id) if course_module is None: # course is None if not found return Response(status=status.HTTP_404_NOT_FOUND) if not has_team_api_access(request.user, course_id): return Response(status=status.HTTP_403_FORBIDDEN) ordering = request.QUERY_PARAMS.get('order_by', 'name') if ordering == 'name': topics = get_ordered_topics(course_module, ordering) else: return Response( { 'developer_message': "unsupported order_by value {}".format(ordering), 'user_message': _(u"The ordering {} is not supported").format(ordering), }, status=status.HTTP_400_BAD_REQUEST) page = self.paginate_queryset(topics) serializer = self.get_pagination_serializer(page) serializer.context = {'sort_order': ordering} return Response(serializer.data) # pylint: disable=maybe-no-member
def get(self, request): """GET /api/team/v0/team_membership""" queryset = CourseTeamMembership.objects.all() specified_username_or_team = False if 'team_id' in request.QUERY_PARAMS: specified_username_or_team = True team_id = request.QUERY_PARAMS['team_id'] try: team = CourseTeam.objects.get(team_id=team_id) except CourseTeam.DoesNotExist: return Response(status=status.HTTP_404_NOT_FOUND) if not has_team_api_access(request.user, team.course_id): return Response(status=status.HTTP_404_NOT_FOUND) queryset = queryset.filter(team__team_id=team_id) if 'username' in request.QUERY_PARAMS: specified_username_or_team = True if not request.user.is_staff: enrolled_courses = ( CourseEnrollment.enrollments_for_user(request.user).values_list('course_id', flat=True) ) staff_courses = ( CourseAccessRole.objects.filter(user=request.user, role='staff').values_list('course_id', flat=True) ) valid_courses = [ CourseKey.from_string(course_key_string) for course_list in [enrolled_courses, staff_courses] for course_key_string in course_list ] queryset = queryset.filter(team__course_id__in=valid_courses) queryset = queryset.filter(user__username=request.QUERY_PARAMS['username']) if not specified_username_or_team: return Response( build_api_error(ugettext_noop("username or team_id must be specified.")), status=status.HTTP_400_BAD_REQUEST ) page = self.paginate_queryset(queryset) serializer = self.get_pagination_serializer(page) return Response(serializer.data) # pylint: disable=maybe-no-member
def get(self, request): """GET /api/team/v0/team_membership""" queryset = CourseTeamMembership.objects.all() specified_username_or_team = False if 'team_id' in request.QUERY_PARAMS: specified_username_or_team = True team_id = request.QUERY_PARAMS['team_id'] try: team = CourseTeam.objects.get(team_id=team_id) except CourseTeam.DoesNotExist: return Response(status=status.HTTP_404_NOT_FOUND) if not has_team_api_access(request.user, team.course_id): return Response(status=status.HTTP_404_NOT_FOUND) queryset = queryset.filter(team__team_id=team_id) if 'username' in request.QUERY_PARAMS: specified_username_or_team = True if not request.user.is_staff: enrolled_courses = ( CourseEnrollment.enrollments_for_user(request.user).values_list('course_id', flat=True) ) staff_courses = ( CourseAccessRole.objects.filter(user=request.user, role='staff').values_list('course_id', flat=True) ) valid_courses = [ CourseKey.from_string(course_key_string) for course_list in [enrolled_courses, staff_courses] for course_key_string in course_list ] queryset = queryset.filter(team__course_id__in=valid_courses) queryset = queryset.filter(user__username=request.QUERY_PARAMS['username']) if not specified_username_or_team: return Response( build_api_error(ugettext_noop("username or team_id must be specified.")), status=status.HTTP_400_BAD_REQUEST ) page = self.paginate_queryset(queryset) serializer = self.get_pagination_serializer(page) return Response(serializer.data) # pylint: disable=maybe-no-member
def get(self, request): """GET /api/team/v0/topics/?course_id={course_id}""" course_id_string = request.QUERY_PARAMS.get('course_id', None) if course_id_string is None: return Response({ 'field_errors': { 'course_id': build_api_error( ugettext_noop("The supplied course id {course_id} is not valid."), course_id=course_id_string ) } }, status=status.HTTP_400_BAD_REQUEST) try: course_id = CourseKey.from_string(course_id_string) except InvalidKeyError: return Response(status=status.HTTP_404_NOT_FOUND) # Ensure the course exists course_module = modulestore().get_course(course_id) if course_module is None: # course is None if not found return Response(status=status.HTTP_404_NOT_FOUND) if not has_team_api_access(request.user, course_id): return Response(status=status.HTTP_403_FORBIDDEN) ordering = request.QUERY_PARAMS.get('order_by', 'name') if ordering == 'name': topics = get_ordered_topics(course_module, ordering) else: return Response({ 'developer_message': "unsupported order_by value {ordering}".format(ordering=ordering), # Translators: 'ordering' is a string describing a way # of ordering a list. For example, {ordering} may be # 'name', indicating that the user wants to sort the # list by lower case name. 'user_message': _(u"The ordering {ordering} is not supported").format(ordering=ordering), }, status=status.HTTP_400_BAD_REQUEST) page = self.paginate_queryset(topics) serializer = self.pagination_serializer_class(page, context={'course_id': course_id, 'sort_order': ordering}) return Response(serializer.data) # pylint: disable=maybe-no-member
def get(self, request): """GET /api/team/v0/teams/""" result_filter = {} if 'course_id' in request.QUERY_PARAMS: course_id_string = request.QUERY_PARAMS['course_id'] try: course_key = CourseKey.from_string(course_id_string) # Ensure the course exists course_module = modulestore().get_course(course_key) if course_module is None: return Response(status=status.HTTP_404_NOT_FOUND) result_filter.update({'course_id': course_key}) except InvalidKeyError: error = build_api_error( ugettext_noop("The supplied course id {course_id} is not valid."), course_id=course_id_string, ) return Response(error, status=status.HTTP_400_BAD_REQUEST) if not has_team_api_access(request.user, course_key): return Response(status=status.HTTP_403_FORBIDDEN) else: return Response( build_api_error(ugettext_noop("course_id must be provided")), status=status.HTTP_400_BAD_REQUEST ) text_search = request.QUERY_PARAMS.get('text_search', None) if text_search and request.QUERY_PARAMS.get('order_by', None): return Response( build_api_error(ugettext_noop("text_search and order_by cannot be provided together")), status=status.HTTP_400_BAD_REQUEST ) topic_id = request.QUERY_PARAMS.get('topic_id', None) if topic_id is not None: if topic_id not in [topic['id'] for topic in course_module.teams_configuration['topics']]: error = build_api_error( ugettext_noop('The supplied topic id {topic_id} is not valid'), topic_id=topic_id ) return Response(error, status=status.HTTP_400_BAD_REQUEST) result_filter.update({'topic_id': topic_id}) if text_search and CourseTeamIndexer.search_is_enabled(): try: search_engine = CourseTeamIndexer.engine() except ElasticSearchConnectionError: return Response( build_api_error(ugettext_noop('Error connecting to elasticsearch')), status=status.HTTP_400_BAD_REQUEST ) result_filter.update({'course_id': course_id_string}) search_results = search_engine.search( query_string=text_search.encode('utf-8'), field_dictionary=result_filter, size=MAXIMUM_SEARCH_SIZE, ) paginated_results = paginate_search_results( CourseTeam, search_results, self.get_paginate_by(), self.get_page() ) serializer = self.get_pagination_serializer(paginated_results) tracker.emit('edx.team.searched', { "number_of_results": search_results['total'], "search_text": text_search, "topic_id": topic_id, "course_id": course_id_string, }) else: queryset = CourseTeam.objects.filter(**result_filter) order_by_input = request.QUERY_PARAMS.get('order_by', 'name') if order_by_input == 'name': # MySQL does case-insensitive order_by. queryset = queryset.order_by('name') elif order_by_input == 'open_slots': queryset = queryset.order_by('team_size', '-last_activity_at') elif order_by_input == 'last_activity_at': queryset = queryset.order_by('-last_activity_at', 'team_size') else: return Response({ 'developer_message': "unsupported order_by value {ordering}".format(ordering=order_by_input), # Translators: 'ordering' is a string describing a way # of ordering a list. For example, {ordering} may be # 'name', indicating that the user wants to sort the # list by lower case name. 'user_message': _(u"The ordering {ordering} is not supported").format(ordering=order_by_input), }, status=status.HTTP_400_BAD_REQUEST) page = self.paginate_queryset(queryset) serializer = self.get_pagination_serializer(page) serializer.context.update({'sort_order': order_by_input}) # pylint: disable=maybe-no-member return Response(serializer.data) # pylint: disable=maybe-no-member
def get(self, request): """GET /api/team/v0/teams/""" result_filter = { 'is_active': True } if 'course_id' in request.QUERY_PARAMS: course_id_string = request.QUERY_PARAMS['course_id'] try: course_key = CourseKey.from_string(course_id_string) # Ensure the course exists course_module = modulestore().get_course(course_key) if course_module is None: return Response(status=status.HTTP_404_NOT_FOUND) result_filter.update({'course_id': course_key}) except InvalidKeyError: error = build_api_error( ugettext_noop("The supplied course id {course_id} is not valid."), course_id=course_id_string, ) return Response(error, status=status.HTTP_400_BAD_REQUEST) if not has_team_api_access(request.user, course_key): return Response(status=status.HTTP_403_FORBIDDEN) else: return Response( build_api_error(ugettext_noop("course_id must be provided")), status=status.HTTP_400_BAD_REQUEST ) if 'topic_id' in request.QUERY_PARAMS: topic_id = request.QUERY_PARAMS['topic_id'] if topic_id not in [topic['id'] for topic in course_module.teams_configuration['topics']]: error = build_api_error( ugettext_noop('The supplied topic id {topic_id} is not valid'), topic_id=topic_id ) return Response(error, status=status.HTTP_400_BAD_REQUEST) result_filter.update({'topic_id': request.QUERY_PARAMS['topic_id']}) if 'include_inactive' in request.QUERY_PARAMS and request.QUERY_PARAMS['include_inactive'].lower() == 'true': del result_filter['is_active'] if 'text_search' in request.QUERY_PARAMS: return Response( build_api_error(ugettext_noop("text_search is not yet supported.")), status=status.HTTP_400_BAD_REQUEST ) queryset = CourseTeam.objects.filter(**result_filter) order_by_input = request.QUERY_PARAMS.get('order_by', 'name') if order_by_input == 'name': queryset = queryset.extra(select={'lower_name': "lower(name)"}) order_by_field = 'lower_name' elif order_by_input == 'open_slots': queryset = queryset.annotate(team_size=Count('users')) order_by_field = 'team_size' elif order_by_input == 'last_activity': return Response( build_api_error(ugettext_noop("last_activity is not yet supported")), status=status.HTTP_400_BAD_REQUEST ) else: return Response({ 'developer_message': "unsupported order_by value {ordering}".format(ordering=order_by_input), # Translators: 'ordering' is a string describing a way # of ordering a list. For example, {ordering} may be # 'name', indicating that the user wants to sort the # list by lower case name. 'user_message': _(u"The ordering {ordering} is not supported").format(ordering=order_by_input), }, status=status.HTTP_400_BAD_REQUEST) queryset = queryset.order_by(order_by_field) # TODO: Remove this on update to Django 1.8 # Use the cached length of the queryset in order to avoid # making an extra database call to get the number of items in # the collection paginator = self.paginator_class(queryset, self.get_paginate_by()) paginator._count = len(queryset) # pylint: disable=protected-access page = paginator.page(int(request.QUERY_PARAMS.get('page', 1))) # end TODO serializer = self.get_pagination_serializer(page) serializer.context.update({'sort_order': order_by_input}) # pylint: disable=maybe-no-member return Response(serializer.data) # pylint: disable=maybe-no-member
def get(self, request): """GET /api/team/v0/teams/""" result_filter = { 'is_active': True } if 'course_id' in request.QUERY_PARAMS: course_id_string = request.QUERY_PARAMS['course_id'] try: course_key = CourseKey.from_string(course_id_string) # Ensure the course exists course_module = modulestore().get_course(course_key) if course_module is None: return Response(status=status.HTTP_404_NOT_FOUND) result_filter.update({'course_id': course_key}) except InvalidKeyError: error = build_api_error( ugettext_noop("The supplied course id {course_id} is not valid."), course_id=course_id_string, ) return Response(error, status=status.HTTP_400_BAD_REQUEST) if not has_team_api_access(request.user, course_key): return Response(status=status.HTTP_403_FORBIDDEN) else: return Response( build_api_error(ugettext_noop("course_id must be provided")), status=status.HTTP_400_BAD_REQUEST ) if 'topic_id' in request.QUERY_PARAMS: topic_id = request.QUERY_PARAMS['topic_id'] if topic_id not in [topic['id'] for topic in course_module.teams_configuration['topics']]: error = build_api_error( ugettext_noop('The supplied topic id {topic_id} is not valid'), topic_id=topic_id ) return Response(error, status=status.HTTP_400_BAD_REQUEST) result_filter.update({'topic_id': request.QUERY_PARAMS['topic_id']}) if 'include_inactive' in request.QUERY_PARAMS and request.QUERY_PARAMS['include_inactive'].lower() == 'true': del result_filter['is_active'] if 'text_search' in request.QUERY_PARAMS: return Response( build_api_error(ugettext_noop("text_search is not yet supported.")), status=status.HTTP_400_BAD_REQUEST ) queryset = CourseTeam.objects.filter(**result_filter) order_by_input = request.QUERY_PARAMS.get('order_by', 'name') if order_by_input == 'name': queryset = queryset.extra(select={'lower_name': "lower(name)"}) order_by_field = 'lower_name' elif order_by_input == 'open_slots': queryset = queryset.annotate(team_size=Count('users')) order_by_field = 'team_size' elif order_by_input == 'last_activity': return Response( build_api_error(ugettext_noop("last_activity is not yet supported")), status=status.HTTP_400_BAD_REQUEST ) else: return Response({ 'developer_message': "unsupported order_by value {}".format(order_by_input), 'user_message': _(u"The ordering {} is not supported").format(order_by_input), }, status=status.HTTP_400_BAD_REQUEST) queryset = queryset.order_by(order_by_field) page = self.paginate_queryset(queryset) serializer = self.get_pagination_serializer(page) serializer.context.update({'sort_order': order_by_input}) # pylint: disable=maybe-no-member return Response(serializer.data) # pylint: disable=maybe-no-member
def get(self, request): """GET /api/team/v0/team_membership""" specified_username_or_team = False username = None team_id = None requested_course_id = None requested_course_key = None accessible_course_ids = None if 'course_id' in request.QUERY_PARAMS: requested_course_id = request.QUERY_PARAMS['course_id'] try: requested_course_key = CourseKey.from_string( requested_course_id) except InvalidKeyError: return Response(status=status.HTTP_404_NOT_FOUND) if 'team_id' in request.QUERY_PARAMS: specified_username_or_team = True team_id = request.QUERY_PARAMS['team_id'] try: team = CourseTeam.objects.get(team_id=team_id) except CourseTeam.DoesNotExist: return Response(status=status.HTTP_404_NOT_FOUND) if requested_course_key is not None and requested_course_key != team.course_id: return Response(status=status.HTTP_400_BAD_REQUEST) if not has_team_api_access(request.user, team.course_id): return Response(status=status.HTTP_404_NOT_FOUND) if 'username' in request.QUERY_PARAMS: specified_username_or_team = True username = request.QUERY_PARAMS['username'] if not request.user.is_staff: enrolled_courses = (CourseEnrollment.enrollments_for_user( request.user).values_list('course_id', flat=True)) staff_courses = (CourseAccessRole.objects.filter( user=request.user, role='staff').values_list('course_id', flat=True)) accessible_course_ids = [ item for sublist in (enrolled_courses, staff_courses) for item in sublist ] if requested_course_id is not None and requested_course_id not in accessible_course_ids: return Response(status=status.HTTP_400_BAD_REQUEST) if not specified_username_or_team: return Response(build_api_error( ugettext_noop("username or team_id must be specified.")), status=status.HTTP_400_BAD_REQUEST) course_keys = None if requested_course_key is not None: course_keys = [requested_course_key] elif accessible_course_ids is not None: course_keys = [ CourseKey.from_string(course_string) for course_string in accessible_course_ids ] queryset = CourseTeamMembership.get_memberships( username, course_keys, team_id) page = self.paginate_queryset(queryset) serializer = self.get_pagination_serializer(page) return Response(serializer.data) # pylint: disable=maybe-no-member
def get(self, request): """GET /api/team/v0/topics/?course_id={course_id}""" course_id_string = request.QUERY_PARAMS.get('course_id', None) if course_id_string is None: return Response( { 'field_errors': { 'course_id': build_api_error(ugettext_noop( "The supplied course id {course_id} is not valid." ), course_id=course_id_string) } }, status=status.HTTP_400_BAD_REQUEST) try: course_id = CourseKey.from_string(course_id_string) except InvalidKeyError: return Response(status=status.HTTP_404_NOT_FOUND) # Ensure the course exists course_module = modulestore().get_course(course_id) if course_module is None: # course is None if not found return Response(status=status.HTTP_404_NOT_FOUND) if not has_team_api_access(request.user, course_id): return Response(status=status.HTTP_403_FORBIDDEN) ordering = request.QUERY_PARAMS.get('order_by', 'name') if ordering not in ['name', 'team_count']: return Response( { 'developer_message': "unsupported order_by value {ordering}".format( ordering=ordering), # Translators: 'ordering' is a string describing a way # of ordering a list. For example, {ordering} may be # 'name', indicating that the user wants to sort the # list by lower case name. 'user_message': _(u"The ordering {ordering} is not supported").format( ordering=ordering), }, status=status.HTTP_400_BAD_REQUEST) # Always sort alphabetically, as it will be used as secondary sort # in the case of "team_count". topics = get_alphabetical_topics(course_module) if ordering == 'team_count': add_team_count(topics, course_id) topics.sort(key=lambda t: t['team_count'], reverse=True) page = self.paginate_queryset(topics) # Since team_count has already been added to all the topics, use PaginatedTopicSerializer. # Even though sorting is done outside of the serializer, sort_order needs to be passed # to the serializer so that the paginated results indicate how they were sorted. serializer = PaginatedTopicSerializer(page, context={ 'course_id': course_id, 'sort_order': ordering }) else: page = self.paginate_queryset(topics) # Use the serializer that adds team_count in a bulk operation per page. serializer = BulkTeamCountPaginatedTopicSerializer( page, context={ 'course_id': course_id, 'sort_order': ordering }) return Response(serializer.data)
def post(self, request): """POST /api/team/v0/team_membership""" field_errors = {} if "username" not in request.DATA: field_errors["username"] = build_api_error(ugettext_noop("Username is required.")) if "team_id" not in request.DATA: field_errors["team_id"] = build_api_error(ugettext_noop("Team id is required.")) if field_errors: return Response({"field_errors": field_errors}, status=status.HTTP_400_BAD_REQUEST) try: team = CourseTeam.objects.get(team_id=request.DATA["team_id"]) except CourseTeam.DoesNotExist: return Response(status=status.HTTP_404_NOT_FOUND) username = request.DATA["username"] if not has_team_api_access(request.user, team.course_id, access_username=username): return Response(status=status.HTTP_404_NOT_FOUND) try: user = User.objects.get(username=username) except User.DoesNotExist: return Response(status=status.HTTP_404_NOT_FOUND) course_module = modulestore().get_course(team.course_id) if course_module.teams_max_size is not None and team.users.count() >= course_module.teams_max_size: return Response( build_api_error(ugettext_noop("This team is already full.")), status=status.HTTP_400_BAD_REQUEST ) try: membership = team.add_user(user) emit_team_event( "edx.team.learner_added", team.course_id, { "team_id": team.team_id, "user_id": user.id, "add_method": "joined_from_team_view" if user == request.user else "added_by_another_user", }, ) except AlreadyOnTeamInCourse: return Response( build_api_error( ugettext_noop("The user {username} is already a member of a team in this course."), username=username, ), status=status.HTTP_400_BAD_REQUEST, ) except NotEnrolledInCourseForTeam: return Response( build_api_error( ugettext_noop("The user {username} is not enrolled in the course associated with this team."), username=username, ), status=status.HTTP_400_BAD_REQUEST, ) serializer = self.get_serializer(instance=membership) return Response(serializer.data)
def get(self, request): """GET /api/team/v0/teams/""" result_filter = {'is_active': True} if 'course_id' in request.QUERY_PARAMS: course_id_string = request.QUERY_PARAMS['course_id'] try: course_key = CourseKey.from_string(course_id_string) # Ensure the course exists course_module = modulestore().get_course(course_key) if course_module is None: return Response(status=status.HTTP_404_NOT_FOUND) result_filter.update({'course_id': course_key}) except InvalidKeyError: error = build_api_error( ugettext_noop( "The supplied course id {course_id} is not valid."), course_id=course_id_string, ) return Response(error, status=status.HTTP_400_BAD_REQUEST) if not has_team_api_access(request.user, course_key): return Response(status=status.HTTP_403_FORBIDDEN) else: return Response(build_api_error( ugettext_noop("course_id must be provided")), status=status.HTTP_400_BAD_REQUEST) if 'text_search' in request.QUERY_PARAMS and 'order_by' in request.QUERY_PARAMS: return Response(build_api_error( ugettext_noop( "text_search and order_by cannot be provided together")), status=status.HTTP_400_BAD_REQUEST) if 'topic_id' in request.QUERY_PARAMS: topic_id = request.QUERY_PARAMS['topic_id'] if topic_id not in [ topic['id'] for topic in course_module.teams_configuration['topics'] ]: error = build_api_error(ugettext_noop( 'The supplied topic id {topic_id} is not valid'), topic_id=topic_id) return Response(error, status=status.HTTP_400_BAD_REQUEST) result_filter.update( {'topic_id': request.QUERY_PARAMS['topic_id']}) if 'include_inactive' in request.QUERY_PARAMS and request.QUERY_PARAMS[ 'include_inactive'].lower() == 'true': del result_filter['is_active'] if 'text_search' in request.QUERY_PARAMS and CourseTeamIndexer.search_is_enabled( ): search_engine = CourseTeamIndexer.engine() text_search = request.QUERY_PARAMS['text_search'].encode('utf-8') result_filter.update({'course_id': course_id_string}) search_results = search_engine.search( query_string=text_search, field_dictionary=result_filter, size=MAXIMUM_SEARCH_SIZE, ) paginated_results = paginate_search_results( CourseTeam, search_results, self.get_paginate_by(), self.get_page()) serializer = self.get_pagination_serializer(paginated_results) else: queryset = CourseTeam.objects.filter(**result_filter) order_by_input = request.QUERY_PARAMS.get('order_by', 'name') if order_by_input == 'name': queryset = queryset.extra(select={'lower_name': "lower(name)"}) queryset = queryset.order_by('lower_name') elif order_by_input == 'open_slots': queryset = queryset.annotate(team_size=Count('users')) queryset = queryset.order_by('team_size', '-last_activity_at') elif order_by_input == 'last_activity_at': queryset = queryset.annotate(team_size=Count('users')) queryset = queryset.order_by('-last_activity_at', 'team_size') else: return Response( { 'developer_message': "unsupported order_by value {ordering}".format( ordering=order_by_input), # Translators: 'ordering' is a string describing a way # of ordering a list. For example, {ordering} may be # 'name', indicating that the user wants to sort the # list by lower case name. 'user_message': _(u"The ordering {ordering} is not supported").format( ordering=order_by_input), }, status=status.HTTP_400_BAD_REQUEST) page = self.paginate_queryset(queryset) serializer = self.get_pagination_serializer(page) serializer.context.update({'sort_order': order_by_input}) # pylint: disable=maybe-no-member return Response(serializer.data) # pylint: disable=maybe-no-member
def get(self, request): """GET /api/team/v0/teams/""" result_filter = {'is_active': True} if 'course_id' in request.QUERY_PARAMS: course_id_string = request.QUERY_PARAMS['course_id'] try: course_key = CourseKey.from_string(course_id_string) # Ensure the course exists course_module = modulestore().get_course(course_key) if course_module is None: return Response(status=status.HTTP_404_NOT_FOUND) result_filter.update({'course_id': course_key}) except InvalidKeyError: error = build_api_error( ugettext_noop( "The supplied course id {course_id} is not valid."), course_id=course_id_string, ) return Response(error, status=status.HTTP_400_BAD_REQUEST) if not has_team_api_access(request.user, course_key): return Response(status=status.HTTP_403_FORBIDDEN) else: return Response(build_api_error( ugettext_noop("course_id must be provided")), status=status.HTTP_400_BAD_REQUEST) if 'topic_id' in request.QUERY_PARAMS: topic_id = request.QUERY_PARAMS['topic_id'] if topic_id not in [ topic['id'] for topic in course_module.teams_configuration['topics'] ]: error = build_api_error(ugettext_noop( 'The supplied topic id {topic_id} is not valid'), topic_id=topic_id) return Response(error, status=status.HTTP_400_BAD_REQUEST) result_filter.update( {'topic_id': request.QUERY_PARAMS['topic_id']}) if 'include_inactive' in request.QUERY_PARAMS and request.QUERY_PARAMS[ 'include_inactive'].lower() == 'true': del result_filter['is_active'] if 'text_search' in request.QUERY_PARAMS: return Response(build_api_error( ugettext_noop("text_search is not yet supported.")), status=status.HTTP_400_BAD_REQUEST) queryset = CourseTeam.objects.filter(**result_filter) order_by_input = request.QUERY_PARAMS.get('order_by', 'name') if order_by_input == 'name': queryset = queryset.extra(select={'lower_name': "lower(name)"}) order_by_field = 'lower_name' elif order_by_input == 'open_slots': queryset = queryset.annotate(team_size=Count('users')) order_by_field = 'team_size' elif order_by_input == 'last_activity': return Response(build_api_error( ugettext_noop("last_activity is not yet supported")), status=status.HTTP_400_BAD_REQUEST) else: return Response( { 'developer_message': "unsupported order_by value {ordering}".format( ordering=order_by_input), # Translators: 'ordering' is a string describing a way # of ordering a list. For example, {ordering} may be # 'name', indicating that the user wants to sort the # list by lower case name. 'user_message': _(u"The ordering {ordering} is not supported").format( ordering=order_by_input), }, status=status.HTTP_400_BAD_REQUEST) queryset = queryset.order_by(order_by_field) # TODO: Remove this on update to Django 1.8 # Use the cached length of the queryset in order to avoid # making an extra database call to get the number of items in # the collection paginator = self.paginator_class(queryset, self.get_paginate_by()) paginator._count = len(queryset) # pylint: disable=protected-access page = paginator.page(int(request.QUERY_PARAMS.get('page', 1))) # end TODO serializer = self.get_pagination_serializer(page) serializer.context.update({'sort_order': order_by_input}) # pylint: disable=maybe-no-member return Response(serializer.data) # pylint: disable=maybe-no-member