Commit 9bc7a518 by David Adams

Fixes issue with metrics tab click handlers

  Click handlers were not getting attached to DOM elements in some cases on slow running machines.
  Added logic to attach handlers when elements are ready.

Added 2 buttons on metrics tab:
  Download Subsection Data for downloading to csv.
  Download Problem Data for downloading to csv.
parent aacbc5ed
......@@ -95,12 +95,15 @@ class TestGetProblemGradeDistribution(ModuleStoreTestCase):
def test_get_problem_grade_distribution(self):
prob_grade_distrib = get_problem_grade_distribution(self.course.id)
prob_grade_distrib, total_student_count = get_problem_grade_distribution(self.course.id)
for problem in prob_grade_distrib:
max_grade = prob_grade_distrib[problem]['max_grade']
self.assertEquals(1, max_grade)
for val in total_student_count.values():
self.assertEquals(USER_COUNT, val)
def test_get_sequential_open_distibution(self):
sequential_open_distrib = get_sequential_open_distrib(self.course.id)
......@@ -243,6 +246,61 @@ class TestGetProblemGradeDistribution(ModuleStoreTestCase):
# Check response contains 1 line for each user +1 for the header
self.assertEquals(USER_COUNT + 1, len(response.content.splitlines()))
def test_post_metrics_data_subsections_csv(self):
url = reverse('post_metrics_data_csv')
sections = json.dumps(["Introduction"])
tooltips = json.dumps([[{"subsection_name": "Pre-Course Survey", "subsection_num": 1, "type": "subsection", "num_students": 18963}]])
course_id = self.course.id
data_type = 'subsection'
data = json.dumps({'sections': sections,
'tooltips': tooltips,
'course_id': course_id,
'data_type': data_type,
})
response = self.client.post(url, {'data': data})
# Check response contains 1 line for header, 1 line for Section and 1 line for Subsection
self.assertEquals(3, len(response.content.splitlines()))
def test_post_metrics_data_problems_csv(self):
url = reverse('post_metrics_data_csv')
sections = json.dumps(["Introduction"])
tooltips = json.dumps([[[
{'student_count_percent': 0,
'problem_name': 'Q1',
'grade': 0,
'percent': 0,
'label': 'P1.2.1',
'max_grade': 1,
'count_grade': 26,
'type': u'problem'},
{'student_count_percent': 99,
'problem_name': 'Q1',
'grade': 1,
'percent': 100,
'label': 'P1.2.1',
'max_grade': 1,
'count_grade': 4763,
'type': 'problem'},
]]])
course_id = self.course.id
data_type = 'problem'
data = json.dumps({'sections': sections,
'tooltips': tooltips,
'course_id': course_id,
'data_type': data_type,
})
response = self.client.post(url, {'data': data})
# Check response contains 1 line for header, 1 line for Sections and 2 lines for problems
self.assertEquals(4, len(response.content.splitlines()))
def test_get_section_display_name(self):
section_display_name = get_section_display_name(self.course.id)
......
"""
Class Dashboard API endpoint urls.
"""
from django.conf.urls import patterns, url
urlpatterns = patterns('', # nopep8
# Json request data for metrics for entire course
url(r'^(?P<course_id>[^/]+/[^/]+/[^/]+)/all_sequential_open_distrib$',
'class_dashboard.views.all_sequential_open_distrib', name="all_sequential_open_distrib"),
url(r'^(?P<course_id>[^/]+/[^/]+/[^/]+)/all_problem_grade_distribution$',
'class_dashboard.views.all_problem_grade_distribution', name="all_problem_grade_distribution"),
# Json request data for metrics for particular section
url(r'^(?P<course_id>[^/]+/[^/]+/[^/]+)/problem_grade_distribution/(?P<section>\d+)$',
'class_dashboard.views.section_problem_grade_distrib', name="section_problem_grade_distrib"),
# For listing students that opened a sub-section
url(r'^get_students_opened_subsection$',
'class_dashboard.dashboard_data.get_students_opened_subsection', name="get_students_opened_subsection"),
# For listing of students' grade per problem
url(r'^get_students_problem_grades$',
'class_dashboard.dashboard_data.get_students_problem_grades', name="get_students_problem_grades"),
# For generating metrics data as a csv
url(r'^post_metrics_data_csv_url',
'class_dashboard.dashboard_data.post_metrics_data_csv', name="post_metrics_data_csv"),
)
......@@ -250,10 +250,12 @@ def _section_metrics(course_id, access):
'section_key': 'metrics',
'section_display_name': ('Metrics'),
'access': access,
'course_id': course_id,
'sub_section_display_name': get_section_display_name(course_id),
'section_has_problem': get_array_section_has_problem(course_id),
'get_students_opened_subsection_url': reverse('get_students_opened_subsection'),
'get_students_problem_grades_url': reverse('get_students_problem_grades'),
'post_metrics_data_csv_url': reverse('post_metrics_data_csv'),
}
return section_data
......
......@@ -591,17 +591,16 @@ section.instructor-dashboard-content-2 {
.instructor-dashboard-wrapper-2 section.idash-section#metrics {
.metrics-container {
.metrics-container, .metrics-header-container {
position: relative;
width: 100%;
float: left;
clear: both;
margin-top: 25px;
.metrics-left {
.metrics-left, .metrics-left-header {
position: relative;
width: 30%;
height: 640px;
float: left;
margin-right: 2.5%;
......@@ -609,10 +608,13 @@ section.instructor-dashboard-content-2 {
width: 100%;
}
}
.metrics-right {
.metrics-section.metrics-left {
height: 640px;
}
.metrics-right, .metrics-right-header {
position: relative;
width: 65%;
height: 295px;
float: left;
margin-left: 2.5%;
margin-bottom: 25px;
......@@ -622,6 +624,10 @@ section.instructor-dashboard-content-2 {
}
}
.metrics-section.metrics-right {
height: 295px;
}
svg {
.stacked-bar {
cursor: pointer;
......@@ -718,10 +724,6 @@ section.instructor-dashboard-content-2 {
border-radius: 5px;
margin-top: 25px;
}
input#graph_reload {
display: none;
}
}
}
......
<%page args="id_opened_prefix, id_grade_prefix, id_attempt_prefix, id_tooltip_prefix, course_id, **kwargs"/>
<%page args="id_opened_prefix, id_grade_prefix, id_attempt_prefix, id_tooltip_prefix, course_id, allSubsectionTooltipArr, allProblemTooltipArr, **kwargs"/>
<%!
import json
from django.core.urlresolvers import reverse
......@@ -30,6 +30,13 @@ $(function () {
margin: {left:0},
};
// Construct array of tooltips for all sections for the "Download Subsection Data" button.
var sectionTooltipArr = new Array();
paramOpened.data.forEach( function(element, index, array) {
sectionTooltipArr[index] = element.stackData[0].tooltip;
});
allSubsectionTooltipArr[i] = sectionTooltipArr;
barGraphOpened = edx_d3CreateStackedBarGraph(paramOpened, d3.select(curr_id).append("svg"),
d3.select("#${id_tooltip_prefix}"+i));
barGraphOpened.scale.stackColor.range(["#555555","#555555"]);
......@@ -68,6 +75,17 @@ $(function () {
bVerticalXAxisLabel : true,
};
// Construct array of tooltips for all sections for the "Download Problem Data" button.
var sectionTooltipArr = new Array();
paramGrade.data.forEach( function(element, index, array) {
var stackDataArr = new Array();
for (var j = 0; j < element.stackData.length; j++) {
stackDataArr[j] = element.stackData[j].tooltip
}
sectionTooltipArr[index] = stackDataArr;
});
allProblemTooltipArr[i] = sectionTooltipArr;
barGraphGrade = edx_d3CreateStackedBarGraph(paramGrade, d3.select(curr_id).append("svg"),
d3.select("#${id_tooltip_prefix}"+i));
barGraphGrade.scale.stackColor.domain([0,50,100]).range(["#e13f29","#cccccc","#17a74d"]);
......@@ -83,6 +101,7 @@ $(function () {
i+=1;
}
});
});
\ No newline at end of file
......@@ -349,8 +349,20 @@ edx_d3CreateStackedBarGraph = function(parameters, svg, divTooltip) {
var top = pos[1]-10;
var width = $('#'+graph.divTooltip.attr("id")).width();
// Construct the tooltip
if (d.tooltip['type'] == 'subsection') {
tooltip_str = d.tooltip['num_students'] + ' ' + gettext('student(s) opened Subsection') + ' ' \
+ d.tooltip['subsection_num'] + ': ' + d.tooltip['subsection_name']
}else if (d.tooltip['type'] == 'problem') {
tooltip_str = d.tooltip['label'] + ' ' + d.tooltip['problem_name'] + ' - ' \
+ d.tooltip['count_grade'] + ' ' + gettext('students') + ' (' \
+ d.tooltip['student_count_percent'] + '%) (' + \
+ d.tooltip['percent'] + '%: ' + \
+ d.tooltip['grade'] +'/' + d.tooltip['max_grade'] + ' '
+ gettext('questions') + ')'
}
graph.divTooltip.style("visibility", "visible")
.text(d.tooltip);
.text(tooltip_str);
if ((left+width+30) > $("#"+graph.divTooltip.node().parentNode.id).width())
left -= (width+30);
......
......@@ -725,7 +725,9 @@ function goto( mode)
</div>
%endfor
<script>
${all_section_metrics.body("metric_opened_","metric_grade_","metric_attempts_","metric_tooltip_",course.id)}
var allSubsectionTooltipArr = new Array();
var allProblemTooltipArr = new Array();
${all_section_metrics.body("metric_opened_","metric_grade_","metric_attempts_","metric_tooltip_",course.id, allSubsectionTooltipArr, allProblemTooltipArr)}
</script>
%endif
......
......@@ -378,23 +378,7 @@ if settings.COURSEWARE_ENABLED and settings.FEATURES.get('ENABLE_INSTRUCTOR_LEGA
if settings.FEATURES.get('CLASS_DASHBOARD'):
urlpatterns += (
# Json request data for metrics for entire course
url(r'^courses/(?P<course_id>[^/]+/[^/]+/[^/]+)/all_sequential_open_distrib$',
'class_dashboard.views.all_sequential_open_distrib', name="all_sequential_open_distrib"),
url(r'^courses/(?P<course_id>[^/]+/[^/]+/[^/]+)/all_problem_grade_distribution$',
'class_dashboard.views.all_problem_grade_distribution', name="all_problem_grade_distribution"),
# Json request data for metrics for particular section
url(r'^courses/(?P<course_id>[^/]+/[^/]+/[^/]+)/problem_grade_distribution/(?P<section>\d+)$',
'class_dashboard.views.section_problem_grade_distrib', name="section_problem_grade_distrib"),
# For listing students that opened a sub-section
url(r'^get_students_opened_subsection$',
'class_dashboard.dashboard_data.get_students_opened_subsection', name="get_students_opened_subsection"),
# For listing of students' grade per problem
url(r'^get_students_problem_grades$',
'class_dashboard.dashboard_data.get_students_problem_grades', name="get_students_problem_grades"),
url(r'^class_dashboard/', include('class_dashboard.urls')),
)
if settings.DEBUG or settings.FEATURES.get('ENABLE_DJANGO_ADMIN_SITE'):
......
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