diff --git a/lms/djangoapps/instructor/tests/views/test_instructor_dashboard.py b/lms/djangoapps/instructor/tests/views/test_instructor_dashboard.py
index 7a61c2903f..cf49ef2018 100644
--- a/lms/djangoapps/instructor/tests/views/test_instructor_dashboard.py
+++ b/lms/djangoapps/instructor/tests/views/test_instructor_dashboard.py
@@ -321,6 +321,30 @@ class TestInstructorDashboard(ModuleStoreTestCase, LoginEnrollmentTestCase, XssT
# 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
+ def test_open_response_assessment_page(self):
+ """
+ Test that Open Responses is available only if course contains at least one ORA block
+ """
+ ora_section = (
+ '
'
+ ''
+ '
'
+ )
+
+ response = self.client.get(self.url)
+ self.assertNotIn(ora_section, response.content)
+
+ course = ItemFactory.create(
+ parent_location=self.course.location,
+ category="course",
+ display_name="Test course",
+ )
+ ItemFactory.create(parent_location=course.location, category="openassessment")
+ response = self.client.get(self.url)
+ self.assertIn(ora_section, response.content)
+
@ddt.ddt
class TestInstructorDashboardPerformance(ModuleStoreTestCase, LoginEnrollmentTestCase, XssTestMixin):
diff --git a/lms/djangoapps/instructor/views/instructor_dashboard.py b/lms/djangoapps/instructor/views/instructor_dashboard.py
index fdb241f5c1..c1d70f1aef 100644
--- a/lms/djangoapps/instructor/views/instructor_dashboard.py
+++ b/lms/djangoapps/instructor/views/instructor_dashboard.py
@@ -38,6 +38,7 @@ from student.models import CourseEnrollment
from shoppingcart.models import Coupon, PaidCourseRegistration, CourseRegCodeItem
from course_modes.models import CourseMode, CourseModesArchive
from student.roles import CourseFinanceAdminRole, CourseSalesAdminRole
+from lms.djangoapps.courseware.module_render import get_module_by_usage_id
from certificates.models import (
CertificateGenerationConfiguration,
CertificateWhitelist,
@@ -190,6 +191,10 @@ def instructor_dashboard_2(request, course_id):
if certs_enabled and access['admin']:
sections.append(_section_certificates(course))
+ openassessment_blocks = modulestore().get_items(course_key, qualifiers={'category': 'openassessment'})
+ if len(openassessment_blocks) > 0:
+ sections.append(_section_open_response_assessment(request, course, openassessment_blocks, access))
+
disable_buttons = not _is_small_course(course_key)
certificate_white_list = CertificateWhitelist.get_certificate_white_list(course_key)
@@ -698,6 +703,48 @@ def _section_metrics(course, access):
return section_data
+def _section_open_response_assessment(request, course, openassessment_blocks, access):
+ """Provide data for the corresponding dashboard section """
+ course_key = course.id
+
+ ora_items = []
+ parents = {}
+
+ for block in openassessment_blocks:
+ block_parent_id = unicode(block.parent)
+ result_item_id = unicode(block.location)
+ if block_parent_id not in parents:
+ parents[block_parent_id] = modulestore().get_item(block.parent)
+
+ ora_items.append({
+ 'id': result_item_id,
+ 'name': block.display_name,
+ 'parent_id': block_parent_id,
+ 'parent_name': parents[block_parent_id].display_name,
+ 'staff_assessment': 'staff-assessment' in block.assessment_steps,
+ 'url_base': reverse('xblock_view', args=[course.id, block.location, 'student_view']),
+ 'url_grade_available_responses': reverse('xblock_view', args=[course.id, block.location,
+ 'grade_available_responses_view']),
+ })
+
+ openassessment_block = openassessment_blocks[0]
+ block, __ = get_module_by_usage_id(
+ request, unicode(course_key), unicode(openassessment_block.location),
+ disable_staff_debug_info=True, course=course
+ )
+ section_data = {
+ 'fragment': block.render('ora_blocks_listing_view', context={
+ 'ora_items': ora_items,
+ 'ora_item_view_enabled': settings.FEATURES.get('ENABLE_XBLOCK_VIEW_ENDPOINT', False)
+ }),
+ 'section_key': 'open_response_assessment',
+ 'section_display_name': _('Open Responses'),
+ 'access': access,
+ 'course_id': unicode(course_key),
+ }
+ return section_data
+
+
def is_ecommerce_course(course_key):
"""
Checks if the given course is an e-commerce course or not, by checking its SKU value from
diff --git a/lms/static/js/instructor_dashboard/instructor_dashboard.js b/lms/static/js/instructor_dashboard/instructor_dashboard.js
index c075ee2680..272e241855 100644
--- a/lms/static/js/instructor_dashboard/instructor_dashboard.js
+++ b/lms/static/js/instructor_dashboard/instructor_dashboard.js
@@ -190,6 +190,9 @@ such that the value can be defined later than this assignment (file load order).
}, {
constructor: window.InstructorDashboard.sections.Certificates,
$element: idashContent.find('.' + CSS_IDASH_SECTION + '#certificates')
+ }, {
+ constructor: window.InstructorDashboard.sections.OpenResponseAssessment,
+ $element: idashContent.find('.' + CSS_IDASH_SECTION + '#open_response_assessment')
}
];
if (edx.instructor_dashboard.proctoring !== void 0) {
diff --git a/lms/static/js/instructor_dashboard/open_response_assessment.js b/lms/static/js/instructor_dashboard/open_response_assessment.js
new file mode 100644
index 0000000000..c1290f7e29
--- /dev/null
+++ b/lms/static/js/instructor_dashboard/open_response_assessment.js
@@ -0,0 +1,40 @@
+/* globals _ */
+
+(function(_) {
+ 'use strict';
+
+ var OpenResponseAssessment = (function() {
+ function OpenResponseAssessmentBlock($section) {
+ this.$section = $section;
+ this.$section.data('wrapper', this);
+ }
+
+ OpenResponseAssessmentBlock.prototype.onClickTitle = function() {
+ var block = this.$section.find('.open-response-assessment');
+ XBlock.initializeBlock($(block).find('.xblock')[0]);
+ };
+
+ return OpenResponseAssessmentBlock;
+ }());
+
+ if (typeof window.setup_debug === 'undefined') {
+ // eslint-disable-next-line no-unused-vars, camelcase
+ window.setup_debug = function(element_id, edit_link, staff_context) {
+ // stub function.
+ };
+ }
+
+ _.defaults(window, {
+ InstructorDashboard: {}
+ });
+
+ _.defaults(window.InstructorDashboard, {
+ sections: {}
+ });
+
+ _.defaults(window.InstructorDashboard.sections, {
+ OpenResponseAssessment: OpenResponseAssessment
+ });
+
+ this.OpenResponseAssessment = OpenResponseAssessment;
+}).call(this, _);
diff --git a/lms/static/sass/course/instructor/_instructor_2.scss b/lms/static/sass/course/instructor/_instructor_2.scss
index 32f6540973..55caeeb5f2 100644
--- a/lms/static/sass/course/instructor/_instructor_2.scss
+++ b/lms/static/sass/course/instructor/_instructor_2.scss
@@ -1482,6 +1482,14 @@
}
}
+// view - student admin
+// --------------------
+.instructor-dashboard-wrapper-2 section.idash-section#open_response_assessment {
+ .open-response-assessment {
+ padding-top: 20px;
+ }
+}
+
input[name="subject"] {
width:600px;
}
diff --git a/lms/templates/instructor/instructor_dashboard_2/instructor_dashboard_2.html b/lms/templates/instructor/instructor_dashboard_2/instructor_dashboard_2.html
index 9df3aa8daf..bdd6cde429 100644
--- a/lms/templates/instructor/instructor_dashboard_2/instructor_dashboard_2.html
+++ b/lms/templates/instructor/instructor_dashboard_2/instructor_dashboard_2.html
@@ -5,6 +5,7 @@
<%!
from django.utils.translation import ugettext as _
from django.core.urlresolvers import reverse
+from openedx.core.djangolib.markup import HTML
%>
<%block name="bodyclass">view-in-course view-instructordash%block>
@@ -63,6 +64,19 @@ from django.core.urlresolvers import reverse
+ % for section_data in sections:
+ % if 'fragment' in section_data:
+ ${HTML(section_data['fragment'].head_html())}
+ % endif
+ % endfor
+%block>
+
+<%block name="js_extra">
+ % for section_data in sections:
+ % if 'fragment' in section_data:
+ ${HTML(section_data['fragment'].foot_html())}
+ % endif
+ % endfor
%block>
## Include Underscore templates
diff --git a/lms/templates/instructor/instructor_dashboard_2/open_response_assessment.html b/lms/templates/instructor/instructor_dashboard_2/open_response_assessment.html
new file mode 100644
index 0000000000..24b71e92f9
--- /dev/null
+++ b/lms/templates/instructor/instructor_dashboard_2/open_response_assessment.html
@@ -0,0 +1,6 @@
+<%page args="section_data" expression_filter="h"/>
+<%! from openedx.core.djangolib.markup import HTML %>
+
+
+ ${HTML(section_data['fragment'].body_html())}
+