diff --git a/lms/djangoapps/instructor/tests/views/test_instructor_dashboard.py b/lms/djangoapps/instructor/tests/views/test_instructor_dashboard.py
new file mode 100644
index 0000000000..535676a320
--- /dev/null
+++ b/lms/djangoapps/instructor/tests/views/test_instructor_dashboard.py
@@ -0,0 +1,131 @@
+"""
+Unit tests for instructor_dashboard.py.
+"""
+from mock import patch
+
+from django.conf import settings
+from django.core.urlresolvers import reverse
+from django.test.utils import override_settings
+from courseware.tests.helpers import LoginEnrollmentTestCase
+
+from student.tests.factories import AdminFactory
+from xmodule.modulestore.tests.django_utils import ModuleStoreTestCase
+from xmodule.modulestore.tests.factories import CourseFactory
+
+
+class TestInstructorDashboard(ModuleStoreTestCase, LoginEnrollmentTestCase):
+ """
+ Tests for the instructor dashboard (not legacy).
+ """
+
+ def setUp(self):
+ """
+ Set up tests
+ """
+ self.course = CourseFactory.create()
+
+ # Create instructor account
+ instructor = AdminFactory.create()
+ self.client.login(username=instructor.username, password="test")
+
+ # URL for instructor dash
+ self.url = reverse('instructor_dashboard', kwargs={'course_id': self.course.id.to_deprecated_string()})
+
+ def tearDown(self):
+ """
+ Undo patches.
+ """
+ patch.stopall()
+
+ def get_dashboard_enrollment_message(self):
+ """
+ Returns expected dashboard enrollment message with link to Insights.
+ """
+ return 'Enrollment data is now available in Example.'.format(unicode(self.course.id))
+
+ def get_dashboard_demographic_message(self):
+ """
+ Returns expected dashboard demographic message with link to Insights.
+ """
+ return 'Demographic data is now available in Example.'.format(unicode(self.course.id))
+
+ @patch.dict(settings.FEATURES, {'DISPLAY_ANALYTICS_ENROLLMENTS': False})
+ @override_settings(ANALYTICS_DASHBOARD_URL='')
+ def test_no_enrollments(self):
+ """
+ Test enrollment section is hidden.
+ """
+ response = self.client.get(self.url)
+ # no enrollment information should be visible
+ self.assertFalse('
Enrollment Information
' in response.content)
+
+ @patch.dict(settings.FEATURES, {'DISPLAY_ANALYTICS_ENROLLMENTS': True})
+ @override_settings(ANALYTICS_DASHBOARD_URL='')
+ def test_show_enrollments_data(self):
+ """
+ Test enrollment data is shown.
+ """
+ response = self.client.get(self.url)
+
+ # enrollment information visible
+ self.assertTrue('Enrollment Information
' in response.content)
+ self.assertTrue('Verified | ' in response.content)
+ self.assertTrue('Audit | ' in response.content)
+ self.assertTrue('Honor | ' in response.content)
+
+ # dashboard link hidden
+ self.assertFalse(self.get_dashboard_enrollment_message() in response.content)
+
+ @patch.dict(settings.FEATURES, {'DISPLAY_ANALYTICS_ENROLLMENTS': False})
+ @override_settings(ANALYTICS_DASHBOARD_URL='http://example.com')
+ @override_settings(ANALYTICS_DASHBOARD_NAME='Example')
+ def test_show_dashboard_enrollment_message(self):
+ """
+ Test enrollment dashboard message is shown and data is hidden.
+ """
+ response = self.client.get(self.url)
+
+ # enrollment information hidden
+ self.assertFalse('Verified | ' in response.content)
+ self.assertFalse('Audit | ' in response.content)
+ self.assertFalse('Honor | ' in response.content)
+
+ # link to dashboard shown
+ expected_message = self.get_dashboard_enrollment_message()
+ self.assertTrue(expected_message in response.content)
+
+ @patch.dict(settings.FEATURES, {'DISPLAY_ANALYTICS_DEMOGRAPHICS': True})
+ @override_settings(ANALYTICS_DASHBOARD_URL='')
+ @override_settings(ANALYTICS_DASHBOARD_NAME='')
+ def test_show_dashboard_demographic_data(self):
+ """
+ Test enrollment demographic data is shown.
+ """
+ response = self.client.get(self.url)
+ # demographic information displayed
+ self.assertTrue('data-feature="year_of_birth"' in response.content)
+ self.assertTrue('data-feature="gender"' in response.content)
+ self.assertTrue('data-feature="level_of_education"' in response.content)
+
+ # dashboard link hidden
+ self.assertFalse(self.get_dashboard_demographic_message() in response.content)
+
+ @patch.dict(settings.FEATURES, {'DISPLAY_ANALYTICS_DEMOGRAPHICS': False})
+ @override_settings(ANALYTICS_DASHBOARD_URL='http://example.com')
+ @override_settings(ANALYTICS_DASHBOARD_NAME='Example')
+ def test_show_dashboard_demographic_message(self):
+ """
+ Test enrollment demographic dashboard message is shown and data is hidden.
+ """
+ response = self.client.get(self.url)
+
+ # demographics are hidden
+ self.assertFalse('data-feature="year_of_birth"' in response.content)
+ self.assertFalse('data-feature="gender"' in response.content)
+ self.assertFalse('data-feature="level_of_education"' in response.content)
+
+ # link to dashboard shown
+ expected_message = self.get_dashboard_demographic_message()
+ self.assertTrue(expected_message in response.content)
diff --git a/lms/djangoapps/instructor/views/instructor_dashboard.py b/lms/djangoapps/instructor/views/instructor_dashboard.py
index 8884adf796..6c97199ac3 100644
--- a/lms/djangoapps/instructor/views/instructor_dashboard.py
+++ b/lms/djangoapps/instructor/views/instructor_dashboard.py
@@ -91,11 +91,7 @@ def instructor_dashboard_2(request, course_id):
if course_mode_has_price:
sections.append(_section_e_commerce(course, access))
- enrollment_count = sections[0]['enrollment_count']['total']
- disable_buttons = False
- max_enrollment_for_buttons = settings.FEATURES.get("MAX_ENROLLMENT_INSTR_BUTTONS")
- if max_enrollment_for_buttons is not None:
- disable_buttons = enrollment_count > max_enrollment_for_buttons
+ disable_buttons = not _is_small_course(course_key)
analytics_dashboard_message = None
if settings.ANALYTICS_DASHBOARD_URL:
@@ -217,12 +213,19 @@ def _section_course_info(course, access):
'access': access,
'course_id': course_key,
'course_display_name': course.display_name,
- 'enrollment_count': CourseEnrollment.enrollment_counts(course_key),
'has_started': course.has_started(),
'has_ended': course.has_ended(),
'list_instructor_tasks_url': reverse('list_instructor_tasks', kwargs={'course_id': course_key.to_deprecated_string()}),
}
+ if settings.FEATURES.get('DISPLAY_ANALYTICS_ENROLLMENTS'):
+ section_data['enrollment_count'] = CourseEnrollment.enrollment_counts(course_key)
+
+ if settings.ANALYTICS_DASHBOARD_URL:
+ dashboard_link = _get_dashboard_link(course_key)
+ message = _("Enrollment data is now available in {dashboard_link}.").format(dashboard_link=dashboard_link)
+ section_data['enrollment_message'] = message
+
try:
advance = lambda memo, (letter, score): "{}: {}, ".format(letter, score) + memo
section_data['grade_cutoffs'] = reduce(advance, course.grade_cutoffs.items(), "")[:-2]
@@ -259,14 +262,20 @@ def _section_membership(course, access):
return section_data
-def _section_student_admin(course, access):
- """ Provide data for the corresponding dashboard section """
- course_key = course.id
+def _is_small_course(course_key):
+ """ Compares against MAX_ENROLLMENT_INSTR_BUTTONS to determine if course enrollment is considered small. """
is_small_course = False
enrollment_count = CourseEnrollment.num_enrolled_in(course_key)
max_enrollment_for_buttons = settings.FEATURES.get("MAX_ENROLLMENT_INSTR_BUTTONS")
if max_enrollment_for_buttons is not None:
is_small_course = enrollment_count <= max_enrollment_for_buttons
+ return is_small_course
+
+
+def _section_student_admin(course, access):
+ """ Provide data for the corresponding dashboard section """
+ course_key = course.id
+ is_small_course = _is_small_course(course_key)
section_data = {
'section_key': 'student_admin',
@@ -354,6 +363,14 @@ def _section_send_email(course, access):
return section_data
+def _get_dashboard_link(course_key):
+ """ Construct a URL to the external analytics dashboard """
+ analytics_dashboard_url = '{0}/courses/{1}'.format(settings.ANALYTICS_DASHBOARD_URL, unicode(course_key))
+ link = "{1}".format(analytics_dashboard_url,
+ settings.ANALYTICS_DASHBOARD_NAME)
+ return link
+
+
def _section_analytics(course, access):
""" Provide data for the corresponding dashboard section """
course_key = course.id
@@ -366,10 +383,7 @@ def _section_analytics(course, access):
}
if settings.ANALYTICS_DASHBOARD_URL:
- # Construct a URL to the external analytics dashboard
- analytics_dashboard_url = '{0}/courses/{1}'.format(settings.ANALYTICS_DASHBOARD_URL, unicode(course_key))
- dashboard_link = "{1}".format(analytics_dashboard_url,
- settings.ANALYTICS_DASHBOARD_NAME)
+ dashboard_link = _get_dashboard_link(course_key)
message = _("Demographic data is now available in {dashboard_link}.").format(dashboard_link=dashboard_link)
section_data['demographic_message'] = message
diff --git a/lms/djangoapps/instructor/views/legacy.py b/lms/djangoapps/instructor/views/legacy.py
index c3e2a65fbd..262983de8e 100644
--- a/lms/djangoapps/instructor/views/legacy.py
+++ b/lms/djangoapps/instructor/views/legacy.py
@@ -134,8 +134,8 @@ def instructor_dashboard(request, course_id):
'header': ['Statistic', 'Value'],
'title': _('Course Statistics At A Glance'),
}
- data = [['# Enrolled', enrollment_number]]
- data += [['Date', timezone.now().isoformat()]]
+
+ data = [['Date', timezone.now().isoformat()]]
data += compute_course_stats(course).items()
if request.user.is_staff:
for field in course.fields.values():
@@ -938,11 +938,10 @@ def instructor_dashboard(request, course_id):
"StudentsDailyActivity", # active students by day
"StudentsDropoffPerDay", # active students dropoff by day
# "OverallGradeDistribution", # overall point distribution for course
- "StudentsActive", # num students active in time period (default = 1wk)
- "StudentsEnrolled", # num students enrolled
# "StudentsPerProblemCorrect", # foreach problem, num students correct
"ProblemGradeDistribution", # foreach problem, grade distribution
]
+
for analytic_name in DASHBOARD_ANALYTICS:
analytics_results[analytic_name] = get_analytics_result(analytic_name)
diff --git a/lms/envs/common.py b/lms/envs/common.py
index c23e6c1556..95cd064d46 100644
--- a/lms/envs/common.py
+++ b/lms/envs/common.py
@@ -286,7 +286,10 @@ FEATURES = {
'ALLOW_AUTOMATED_SIGNUPS': False,
# Display demographic data on the analytics tab in the instructor dashboard.
- 'DISPLAY_ANALYTICS_DEMOGRAPHICS': True
+ 'DISPLAY_ANALYTICS_DEMOGRAPHICS': True,
+
+ # Enable display of enrollment counts in instructor and legacy analytics dashboard
+ 'DISPLAY_ANALYTICS_ENROLLMENTS': True,
}
# Ignore static asset files on import which match this pattern
diff --git a/lms/templates/courseware/instructor_dashboard.html b/lms/templates/courseware/instructor_dashboard.html
index 4439fc6ba7..c3e5a34b48 100644
--- a/lms/templates/courseware/instructor_dashboard.html
+++ b/lms/templates/courseware/instructor_dashboard.html
@@ -594,22 +594,6 @@ function goto( mode)
${_("No Analytics are available at this time.")}
%endif
- %if analytics_results.get("StudentsEnrolled"):
-
- ${_("Students enrolled (historical count, includes those who have since unenrolled):")}
- ${analytics_results["StudentsEnrolled"]['data'][0]['students']}
- (${analytics_results["StudentsEnrolled"]['time']})
-
- %endif
-
- %if analytics_results.get("StudentsActive"):
-
- ${_("Students active in the last week:")}
- ${analytics_results["StudentsActive"]['data'][0]['active']}
- (${analytics_results["StudentsActive"]['time']})
-
- %endif
-
%if analytics_results.get("StudentsDropoffPerDay"):
${_("Student activity day by day")}
diff --git a/lms/templates/instructor/instructor_dashboard_2/course_info.html b/lms/templates/instructor/instructor_dashboard_2/course_info.html
index bd6462934f..f71fb53646 100644
--- a/lms/templates/instructor/instructor_dashboard_2/course_info.html
+++ b/lms/templates/instructor/instructor_dashboard_2/course_info.html
@@ -1,28 +1,35 @@
<%! from django.utils.translation import ugettext as _ %>
<%page args="section_data"/>
-
-
${_("Enrollment Information")}
- ## Translators: 'track' refers to the enrollment type ('honor', 'verified', or 'audit')
-
${_("Number of enrollees (instructors, staff members, and students) by track")}
-
- <% modes = section_data['enrollment_count'] %>
-
-
- | ${_("Verified")} | ${modes['verified']} |
-
-
- | ${_("Audit")} | ${modes['audit']} |
-
-
- | ${_("Honor")} | ${modes['honor']} |
-
-
- | ${_("Total")} | ${modes['total']} |
-
-
-
-
+%if settings.FEATURES.get('DISPLAY_ANALYTICS_ENROLLMENTS') or section_data.get('enrollment_message'):
+
+
${_("Enrollment Information")}
+
+ %if settings.FEATURES.get('DISPLAY_ANALYTICS_ENROLLMENTS'):
+ ## Translators: 'track' refers to the enrollment type ('honor', 'verified', or 'audit')
+
${_("Number of enrollees (instructors, staff members, and students) by track")}
+
+ <% modes = section_data['enrollment_count'] %>
+
+
+ | ${_("Verified")} | ${modes['verified']} |
+
+
+ | ${_("Audit")} | ${modes['audit']} |
+
+
+ | ${_("Honor")} | ${modes['honor']} |
+
+
+ | ${_("Total")} | ${modes['total']} |
+
+
+ %elif section_data.get('enrollment_message'):
+
${section_data['enrollment_message']}
+ %endif
+
+
+%endif
${_("Basic Course Information")}