From 1a4fcb055b2f7f93d58b2e6adf74602e5f9f4d7b Mon Sep 17 00:00:00 2001 From: Sarina Canelake Date: Wed, 14 May 2014 09:27:17 -0400 Subject: [PATCH] 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. --- ...cy_gradebook.py => test_spoc_gradebook.py} | 18 ++++++-- lms/djangoapps/instructor/views/api.py | 43 ++++++++++++++++++- lms/djangoapps/instructor/views/api_urls.py | 4 ++ .../instructor/views/instructor_dashboard.py | 42 ------------------ lms/urls.py | 2 - 5 files changed, 61 insertions(+), 48 deletions(-) rename lms/djangoapps/instructor/tests/{test_legacy_gradebook.py => test_spoc_gradebook.py} (90%) diff --git a/lms/djangoapps/instructor/tests/test_legacy_gradebook.py b/lms/djangoapps/instructor/tests/test_spoc_gradebook.py similarity index 90% rename from lms/djangoapps/instructor/tests/test_legacy_gradebook.py rename to lms/djangoapps/instructor/tests/test_spoc_gradebook.py index fddeef7772..63f7e9198c 100644 --- a/lms/djangoapps/instructor/tests/test_legacy_gradebook.py +++ b/lms/djangoapps/instructor/tests/test_spoc_gradebook.py @@ -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": [ { diff --git a/lms/djangoapps/instructor/views/api.py b/lms/djangoapps/instructor/views/api.py index 0d67a6a1bd..9a2f23e6fc 100644 --- a/lms/djangoapps/instructor/views/api.py +++ b/lms/djangoapps/instructor/views/api.py @@ -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), + }) diff --git a/lms/djangoapps/instructor/views/api_urls.py b/lms/djangoapps/instructor/views/api_urls.py index 96064d2135..ef632e4b91 100644 --- a/lms/djangoapps/instructor/views/api_urls.py +++ b/lms/djangoapps/instructor/views/api_urls.py @@ -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'), ) diff --git a/lms/djangoapps/instructor/views/instructor_dashboard.py b/lms/djangoapps/instructor/views/instructor_dashboard.py index 4b3e95f05f..4fa8a282e4 100644 --- a/lms/djangoapps/instructor/views/instructor_dashboard.py +++ b/lms/djangoapps/instructor/views/instructor_dashboard.py @@ -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), - }) diff --git a/lms/urls.py b/lms/urls.py index 75e1e6dc4c..3c28984657 100644 --- a/lms/urls.py +++ b/lms/urls.py @@ -273,8 +273,6 @@ if settings.COURSEWARE_ENABLED: 'instructor.views.instructor_dashboard.instructor_dashboard_2', name="instructor_dashboard"), url(r'^courses/(?P[^/]+/[^/]+/[^/]+)/instructor/api/', include('instructor.views.api_urls')), - url(r'^courses/(?P[^/]+/[^/]+/[^/]+)/gradebook$', - 'instructor.views.instructor_dashboard.spoc_gradebook', name='spoc_gradebook'), # see ENABLE_INSTRUCTOR_LEGACY_DASHBOARD section for legacy dash urls