def test_paginated_results(self, page_number, page_size, has_next): """ Test the page returned has the expected db objects and acts like a proper page object. """ id_range = get_object_range(page_number, page_size) db_objects = [build_mock_object(obj_id) for obj_id in id_range] self.mock_model.objects.filter = MagicMock(return_value=db_objects) page = paginate_search_results(self.mock_model, self.search_results, page_size, page_number) self.mock_model.objects.filter.assert_called_with(pk__in=id_range) self.assertEquals(db_objects, page.object_list) self.assertTrue(page.number, page_number) self.assertEquals(page.has_next(), has_next)
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 test_invalid_page_number(self, page_num): """ Test that a Http404 error is raised with non-integer and out-of-range pages """ with self.assertRaises(Http404): paginate_search_results(self.mock_model, self.search_results, self.default_size, page_num)
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