Commit 7e9a139d by John Eskew

Merge pull request #11766 from CredoReference/too-many-mongo-queries-in-instructor-api-gradebook

Reduce the number of queries to mongodb in /courses/<course-id>/instructor/api/gradebook API
parents a8f274d5 d7502384
...@@ -2,7 +2,9 @@ ...@@ -2,7 +2,9 @@
Unit tests for instructor_dashboard.py. Unit tests for instructor_dashboard.py.
""" """
import ddt import ddt
import datetime
from mock import patch from mock import patch
from pytz import UTC
from django.conf import settings from django.conf import settings
from django.core.urlresolvers import reverse from django.core.urlresolvers import reverse
...@@ -10,16 +12,16 @@ from django.test.client import RequestFactory ...@@ -10,16 +12,16 @@ from django.test.client import RequestFactory
from django.test.utils import override_settings from django.test.utils import override_settings
from edxmako.shortcuts import render_to_response 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.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 courseware.tests.helpers import LoginEnrollmentTestCase
from instructor.views.gradebook_api import calculate_page_info from instructor.views.gradebook_api import calculate_page_info
from common.test.utils import XssTestMixin from common.test.utils import XssTestMixin
from student.tests.factories import AdminFactory from student.tests.factories import AdminFactory, CourseEnrollmentFactory
from xmodule.modulestore.tests.django_utils import ModuleStoreTestCase from xmodule.modulestore import ModuleStoreEnum
from xmodule.modulestore.tests.factories import CourseFactory 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 shoppingcart.models import PaidCourseRegistration, Order, CourseRegCodeItem
from course_modes.models import CourseMode from course_modes.models import CourseMode
from student.roles import CourseFinanceAdminRole from student.roles import CourseFinanceAdminRole
...@@ -284,7 +286,10 @@ class TestInstructorDashboard(ModuleStoreTestCase, LoginEnrollmentTestCase, XssT ...@@ -284,7 +286,10 @@ class TestInstructorDashboard(ModuleStoreTestCase, LoginEnrollmentTestCase, XssT
@patch('instructor.views.gradebook_api.render_to_response', intercept_renderer) @patch('instructor.views.gradebook_api.render_to_response', intercept_renderer)
@patch('instructor.views.gradebook_api.MAX_STUDENTS_PER_PAGE_GRADE_BOOK', 1) @patch('instructor.views.gradebook_api.MAX_STUDENTS_PER_PAGE_GRADE_BOOK', 1)
def test_spoc_gradebook_pages(self): 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( url = reverse(
'spoc_gradebook', 'spoc_gradebook',
kwargs={'course_id': self.course.id} kwargs={'course_id': self.course.id}
...@@ -293,3 +298,98 @@ class TestInstructorDashboard(ModuleStoreTestCase, LoginEnrollmentTestCase, XssT ...@@ -293,3 +298,98 @@ class TestInstructorDashboard(ModuleStoreTestCase, LoginEnrollmentTestCase, XssT
self.assertEqual(response.status_code, 200) self.assertEqual(response.status_code, 200)
# Max number of student per page is one. Patched setting MAX_STUDENTS_PER_PAGE_GRADE_BOOK = 1 # 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 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='<script>alert("XSS")</script>',
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)
...@@ -15,6 +15,7 @@ from edxmako.shortcuts import render_to_response ...@@ -15,6 +15,7 @@ from edxmako.shortcuts import render_to_response
from courseware.courses import get_course_with_access from courseware.courses import get_course_with_access
from instructor.offline_gradecalc import student_grades from instructor.offline_gradecalc import student_grades
from instructor.views.api import require_level from instructor.views.api import require_level
from xmodule.modulestore.django import modulestore
# Grade book: max students per page # Grade book: max students per page
...@@ -84,6 +85,7 @@ def get_grade_book_page(request, course, course_key): ...@@ -84,6 +85,7 @@ 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. # 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] enrolled_students = enrolled_students[offset: offset + MAX_STUDENTS_PER_PAGE_GRADE_BOOK]
with modulestore().bulk_operations(course.location.course_key):
student_info = [ student_info = [
{ {
'username': student.username, 'username': student.username,
......
Markdown is supported
0% or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment