Make SPOC gradebook an API implementation
rather than just hacked in to the instructor_dashboard base file. Also move the tests from the legacy implementation to the new dash API implementation.
This commit is contained in:
@@ -1,5 +1,5 @@
|
||||
"""
|
||||
Tests of the instructor dashboard gradebook
|
||||
Tests of the instructor dashboard spoc gradebook
|
||||
"""
|
||||
|
||||
from django.test.utils import override_settings
|
||||
@@ -10,7 +10,6 @@ from xmodule.modulestore.tests.django_utils import ModuleStoreTestCase
|
||||
from courseware.tests.tests import TEST_DATA_MIXED_MODULESTORE
|
||||
from capa.tests.response_xml_factory import StringResponseXMLFactory
|
||||
from courseware.tests.factories import StudentModuleFactory
|
||||
from xmodule.modulestore import Location
|
||||
from xmodule.modulestore.django import modulestore
|
||||
|
||||
|
||||
@@ -19,6 +18,11 @@ USER_COUNT = 11
|
||||
|
||||
@override_settings(MODULESTORE=TEST_DATA_MIXED_MODULESTORE)
|
||||
class TestGradebook(ModuleStoreTestCase):
|
||||
"""
|
||||
Test functionality of the spoc gradebook. Sets up a course with assignments and
|
||||
students who've scored various scores on these assignments. Base class for further
|
||||
gradebook tests.
|
||||
"""
|
||||
grading_policy = None
|
||||
|
||||
def setUp(self):
|
||||
@@ -68,7 +72,7 @@ class TestGradebook(ModuleStoreTestCase):
|
||||
)
|
||||
|
||||
self.response = self.client.get(reverse(
|
||||
'gradebook_legacy',
|
||||
'spoc_gradebook',
|
||||
args=(self.course.id.to_deprecated_string(),)
|
||||
))
|
||||
|
||||
@@ -77,6 +81,10 @@ class TestGradebook(ModuleStoreTestCase):
|
||||
|
||||
|
||||
class TestDefaultGradingPolicy(TestGradebook):
|
||||
"""
|
||||
Tests that the grading policy is properly applied for all users in the course
|
||||
Uses the default policy (50% passing rate)
|
||||
"""
|
||||
def test_all_users_listed(self):
|
||||
for user in self.users:
|
||||
self.assertIn(user.username, unicode(self.response.content, 'utf-8'))
|
||||
@@ -98,6 +106,10 @@ class TestDefaultGradingPolicy(TestGradebook):
|
||||
|
||||
|
||||
class TestLetterCutoffPolicy(TestGradebook):
|
||||
"""
|
||||
Tests advanced grading policy (with letter grade cutoffs). Includes tests of
|
||||
UX display (color, etc).
|
||||
"""
|
||||
grading_policy = {
|
||||
"GRADER": [
|
||||
{
|
||||
@@ -31,7 +31,7 @@ from django_comment_common.models import (
|
||||
FORUM_ROLE_MODERATOR,
|
||||
FORUM_ROLE_COMMUNITY_TA,
|
||||
)
|
||||
|
||||
from edxmako.shortcuts import render_to_response
|
||||
from courseware.models import StudentModule
|
||||
from student.models import CourseEnrollment, unique_id_for_user, anonymous_id_for_user
|
||||
import instructor_task.api
|
||||
@@ -46,6 +46,7 @@ from instructor.enrollment import (
|
||||
unenroll_email
|
||||
)
|
||||
from instructor.access import list_with_level, allow_access, revoke_access, update_forum_role
|
||||
from instructor.offline_gradecalc import student_grades
|
||||
import analytics.basic
|
||||
import analytics.distributions
|
||||
import analytics.csvs
|
||||
@@ -1306,3 +1307,43 @@ def _split_input_list(str_list):
|
||||
new_list = [s for s in new_list if s != '']
|
||||
|
||||
return new_list
|
||||
|
||||
|
||||
#---- Gradebook (shown to small courses only) ----
|
||||
@cache_control(no_cache=True, no_store=True, must_revalidate=True)
|
||||
@require_level('staff')
|
||||
def spoc_gradebook(request, course_id):
|
||||
"""
|
||||
Show the gradebook for this course:
|
||||
- Only shown for courses with enrollment < settings.FEATURES.get("MAX_ENROLLMENT_INSTR_BUTTONS")
|
||||
- Only displayed to course staff
|
||||
"""
|
||||
course_key = SlashSeparatedCourseKey.from_deprecated_string(course_id)
|
||||
course = get_course_with_access(request.user, 'staff', course_key, depth=None)
|
||||
|
||||
enrolled_students = User.objects.filter(
|
||||
courseenrollment__course_id=course_key,
|
||||
courseenrollment__is_active=1
|
||||
).order_by('username').select_related("profile")
|
||||
|
||||
# possible extension: implement pagination to show to large courses
|
||||
|
||||
student_info = [
|
||||
{
|
||||
'username': student.username,
|
||||
'id': student.id,
|
||||
'email': student.email,
|
||||
'grade_summary': student_grades(student, request, course),
|
||||
'realname': student.profile.name,
|
||||
}
|
||||
for student in enrolled_students
|
||||
]
|
||||
|
||||
return render_to_response('courseware/gradebook.html', {
|
||||
'students': student_info,
|
||||
'course': course,
|
||||
'course_id': course_key,
|
||||
# Checked above
|
||||
'staff_access': True,
|
||||
'ordered_grades': sorted(course.grade_cutoffs.items(), key=lambda i: i[1], reverse=True),
|
||||
})
|
||||
|
||||
@@ -53,4 +53,8 @@ urlpatterns = patterns('', # nopep8
|
||||
'instructor.views.api.list_report_downloads', name="list_report_downloads"),
|
||||
url(r'calculate_grades_csv$',
|
||||
'instructor.views.api.calculate_grades_csv', name="calculate_grades_csv"),
|
||||
|
||||
# spoc gradebook
|
||||
url(r'^gradebook$',
|
||||
'instructor.views.api.spoc_gradebook', name='spoc_gradebook'),
|
||||
)
|
||||
|
||||
@@ -23,7 +23,6 @@ from courseware.access import has_access
|
||||
from courseware.courses import get_course_by_id, get_cms_course_link, get_course_with_access
|
||||
from django_comment_client.utils import has_forum_access
|
||||
from django_comment_common.models import FORUM_ROLE_ADMINISTRATOR
|
||||
from instructor.offline_gradecalc import student_grades
|
||||
from student.models import CourseEnrollment
|
||||
from bulk_email.models import CourseAuthorization
|
||||
from class_dashboard.dashboard_data import get_section_display_name, get_array_section_has_problem
|
||||
@@ -263,44 +262,3 @@ def _section_metrics(course_key, access):
|
||||
'post_metrics_data_csv_url': reverse('post_metrics_data_csv'),
|
||||
}
|
||||
return section_data
|
||||
|
||||
|
||||
#---- Gradebook (shown to small courses only) ----
|
||||
@cache_control(no_cache=True, no_store=True, must_revalidate=True)
|
||||
def spoc_gradebook(request, course_id):
|
||||
"""
|
||||
Show the gradebook for this course:
|
||||
- Only shown for courses with enrollment < settings.FEATURES.get("MAX_ENROLLMENT_INSTR_BUTTONS")
|
||||
- Only displayed to course staff
|
||||
"""
|
||||
course_key = SlashSeparatedCourseKey.from_deprecated_string(course_id)
|
||||
course = get_course_with_access(request.user, 'staff', course_key, depth=None)
|
||||
|
||||
enrolled_students = User.objects.filter(
|
||||
courseenrollment__course_id=course_key,
|
||||
courseenrollment__is_active=1
|
||||
).order_by('username').select_related("profile")
|
||||
|
||||
# TODO (vshnayder): implement pagination to show to large courses
|
||||
max_num_students = settings.FEATURES.get("MAX_ENROLLMENT_INSTR_BUTTONS")
|
||||
enrolled_students = enrolled_students[:max_num_students] # HACK!
|
||||
|
||||
student_info = [
|
||||
{
|
||||
'username': student.username,
|
||||
'id': student.id,
|
||||
'email': student.email,
|
||||
'grade_summary': student_grades(student, request, course),
|
||||
'realname': student.profile.name,
|
||||
}
|
||||
for student in enrolled_students
|
||||
]
|
||||
|
||||
return render_to_response('courseware/gradebook.html', {
|
||||
'students': student_info,
|
||||
'course': course,
|
||||
'course_id': course_key,
|
||||
# Checked above
|
||||
'staff_access': True,
|
||||
'ordered_grades': sorted(course.grade_cutoffs.items(), key=lambda i: i[1], reverse=True),
|
||||
})
|
||||
|
||||
@@ -273,8 +273,6 @@ if settings.COURSEWARE_ENABLED:
|
||||
'instructor.views.instructor_dashboard.instructor_dashboard_2', name="instructor_dashboard"),
|
||||
url(r'^courses/(?P<course_id>[^/]+/[^/]+/[^/]+)/instructor/api/',
|
||||
include('instructor.views.api_urls')),
|
||||
url(r'^courses/(?P<course_id>[^/]+/[^/]+/[^/]+)/gradebook$',
|
||||
'instructor.views.instructor_dashboard.spoc_gradebook', name='spoc_gradebook'),
|
||||
|
||||
# see ENABLE_INSTRUCTOR_LEGACY_DASHBOARD section for legacy dash urls
|
||||
|
||||
|
||||
Reference in New Issue
Block a user