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
This commit is contained in:
@@ -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='<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
|
||||
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
|
||||
|
||||
|
||||
|
||||
Reference in New Issue
Block a user