Commit 0a909a9b by Sarina Canelake

Merge pull request #3868 from edx/sarina/inst-dash-cleanup

Make SPOC gradebook an API feature
parents 465b2bb7 1cd94cf5
"""
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),
})
......@@ -1423,42 +1423,7 @@ def get_student_grade_summary_data(request, course, get_grades=True, get_raw_sco
#-----------------------------------------------------------------------------
@cache_control(no_cache=True, no_store=True, must_revalidate=True)
def gradebook(request, course_id):
"""
Show the gradebook for this course:
- only displayed to course staff
- shows students who are enrolled.
"""
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.
enrolled_students = enrolled_students[:1000] # 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),
})
# Gradebook has moved to instructor.api.spoc_gradebook #
@cache_control(no_cache=True, no_store=True, must_revalidate=True)
def grade_summary(request, course_key):
......
......@@ -204,7 +204,7 @@ function goto( mode)
% endif
<p>
<a href="${reverse('gradebook_legacy', kwargs=dict(course_id=course.id.to_deprecated_string()))}" class="${'is-disabled' if disable_buttons else ''}">${_("Gradebook")}</a>
<a href="${reverse('spoc_gradebook', kwargs=dict(course_id=course.id.to_deprecated_string()))}" class="${'is-disabled' if disable_buttons else ''}">${_("Gradebook")}</a>
</p>
<p>
......
......@@ -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
......@@ -367,8 +365,6 @@ if settings.COURSEWARE_ENABLED:
if settings.COURSEWARE_ENABLED and settings.FEATURES.get('ENABLE_INSTRUCTOR_LEGACY_DASHBOARD'):
urlpatterns += (
url(r'^courses/(?P<course_id>[^/]+/[^/]+/[^/]+)/legacy_gradebook$',
'instructor.views.legacy.gradebook', name='gradebook_legacy'),
url(r'^courses/(?P<course_id>[^/]+/[^/]+/[^/]+)/legacy_grade_summary$',
'instructor.views.legacy.grade_summary', name='grade_summary_legacy'),
url(r'^courses/(?P<course_id>[^/]+/[^/]+/[^/]+)/legacy_instructor_dash$',
......
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