diff --git a/lms/djangoapps/instructor/tests/views/test_instructor_dashboard.py b/lms/djangoapps/instructor/tests/views/test_instructor_dashboard.py index f0b29ad4b2..e87daae5a6 100644 --- a/lms/djangoapps/instructor/tests/views/test_instructor_dashboard.py +++ b/lms/djangoapps/instructor/tests/views/test_instructor_dashboard.py @@ -2,7 +2,9 @@ Unit tests for instructor_dashboard.py. """ import ddt +import datetime from mock import patch +from pytz import UTC from django.conf import settings from django.core.urlresolvers import reverse @@ -10,16 +12,16 @@ from django.test.client import RequestFactory from django.test.utils import override_settings from edxmako.shortcuts import render_to_response -from lms.djangoapps.ccx.tests.test_views import setup_students_and_grades from courseware.tabs import get_course_tab_list -from courseware.tests.factories import UserFactory +from courseware.tests.factories import UserFactory, StudentModuleFactory from courseware.tests.helpers import LoginEnrollmentTestCase from instructor.views.gradebook_api import calculate_page_info from common.test.utils import XssTestMixin -from student.tests.factories import AdminFactory -from xmodule.modulestore.tests.django_utils import ModuleStoreTestCase -from xmodule.modulestore.tests.factories import CourseFactory +from student.tests.factories import AdminFactory, CourseEnrollmentFactory +from xmodule.modulestore import ModuleStoreEnum +from xmodule.modulestore.tests.django_utils import ModuleStoreTestCase, TEST_DATA_SPLIT_MODULESTORE +from xmodule.modulestore.tests.factories import CourseFactory, ItemFactory, check_mongo_calls from shoppingcart.models import PaidCourseRegistration, Order, CourseRegCodeItem from course_modes.models import CourseMode from student.roles import CourseFinanceAdminRole @@ -284,7 +286,10 @@ class TestInstructorDashboard(ModuleStoreTestCase, LoginEnrollmentTestCase, XssT @patch('instructor.views.gradebook_api.render_to_response', intercept_renderer) @patch('instructor.views.gradebook_api.MAX_STUDENTS_PER_PAGE_GRADE_BOOK', 1) def test_spoc_gradebook_pages(self): - setup_students_and_grades(self) + for i in xrange(2): + username = "user_%d" % i + student = UserFactory.create(username=username) + CourseEnrollmentFactory.create(user=student, course_id=self.course.id) url = reverse( 'spoc_gradebook', kwargs={'course_id': self.course.id} @@ -293,3 +298,98 @@ class TestInstructorDashboard(ModuleStoreTestCase, LoginEnrollmentTestCase, XssT self.assertEqual(response.status_code, 200) # Max number of student per page is one. Patched setting MAX_STUDENTS_PER_PAGE_GRADE_BOOK = 1 self.assertEqual(len(response.mako_context['students']), 1) # pylint: disable=no-member + + +@ddt.ddt +class TestInstructorDashboardPerformance(ModuleStoreTestCase, LoginEnrollmentTestCase, XssTestMixin): + """ + Tests for the instructor dashboard from the performance point of view. + """ + MODULESTORE = TEST_DATA_SPLIT_MODULESTORE + + def setUp(self): + """ + Set up tests + """ + super(TestInstructorDashboardPerformance, self).setUp() + self.course = CourseFactory.create( + grading_policy={"GRADE_CUTOFFS": {"A": 0.75, "B": 0.63, "C": 0.57, "D": 0.5}}, + display_name='', + default_store=ModuleStoreEnum.Type.split + ) + + self.course_mode = CourseMode( + course_id=self.course.id, + mode_slug=CourseMode.DEFAULT_MODE_SLUG, + mode_display_name=CourseMode.DEFAULT_MODE.name, + min_price=40 + ) + self.course_mode.save() + # Create instructor account + self.instructor = AdminFactory.create() + self.client.login(username=self.instructor.username, password="test") + + def test_spoc_gradebook_mongo_calls(self): + """ + Test that the MongoDB cache is used in API to return grades + """ + # prepare course structure + course = ItemFactory.create( + parent_location=self.course.location, + category="course", + display_name="Test course", + ) + + students = [] + for i in xrange(20): + username = "user_%d" % i + student = UserFactory.create(username=username) + CourseEnrollmentFactory.create(user=student, course_id=self.course.id) + students.append(student) + + chapter = ItemFactory.create( + parent=course, + category='chapter', + display_name="Chapter", + publish_item=True, + start=datetime.datetime(2015, 3, 1, tzinfo=UTC), + ) + sequential = ItemFactory.create( + parent=chapter, + category='sequential', + display_name="Lesson", + publish_item=True, + start=datetime.datetime(2015, 3, 1, tzinfo=UTC), + metadata={'graded': True, 'format': 'Homework'}, + ) + vertical = ItemFactory.create( + parent=sequential, + category='vertical', + display_name='Subsection', + publish_item=True, + start=datetime.datetime(2015, 4, 1, tzinfo=UTC), + ) + for i in xrange(10): + problem = ItemFactory.create( + category="problem", + parent=vertical, + display_name="A Problem Block %d" % i, + weight=1, + publish_item=False, + metadata={'rerandomize': 'always'}, + ) + for j in students: + grade = i % 2 + StudentModuleFactory.create( + grade=grade, + max_grade=1, + student=j, + course_id=self.course.id, + module_state_key=problem.location + ) + + # check MongoDB calls count + url = reverse('spoc_gradebook', kwargs={'course_id': self.course.id}) + with check_mongo_calls(8): + response = self.client.get(url) + self.assertEqual(response.status_code, 200) diff --git a/lms/djangoapps/instructor/views/gradebook_api.py b/lms/djangoapps/instructor/views/gradebook_api.py index 5d98534cc9..220bfe4cad 100644 --- a/lms/djangoapps/instructor/views/gradebook_api.py +++ b/lms/djangoapps/instructor/views/gradebook_api.py @@ -15,6 +15,7 @@ from edxmako.shortcuts import render_to_response from courseware.courses import get_course_with_access from instructor.offline_gradecalc import student_grades from instructor.views.api import require_level +from xmodule.modulestore.django import modulestore # Grade book: max students per page @@ -84,15 +85,16 @@ def get_grade_book_page(request, course, course_key): # Apply limit on queryset only if total number of students are greater then MAX_STUDENTS_PER_PAGE_GRADE_BOOK. enrolled_students = enrolled_students[offset: offset + MAX_STUDENTS_PER_PAGE_GRADE_BOOK] - student_info = [ - { - 'username': student.username, - 'id': student.id, - 'email': student.email, - 'grade_summary': student_grades(student, request, course), - } - for student in enrolled_students - ] + with modulestore().bulk_operations(course.location.course_key): + student_info = [ + { + 'username': student.username, + 'id': student.id, + 'email': student.email, + 'grade_summary': student_grades(student, request, course), + } + for student in enrolled_students + ] return student_info, page