<%page args="grade_summary, grade_cutoffs, graph_div_id, show_grade_breakdown = True, show_grade_cutoffs = True, **kwargs"/> <%! import nh3 import json import math import six from openedx.core.djangolib.js_utils import ( dump_js_escaped_json, js_escaped_string ) %> $(function () { function showTooltip(x, y, contents) { $("#tooltip").remove(); var $tooltip_div = $('
').css({ position: 'absolute', display: 'none', top: y + 5, left: x + 15, border: '1px solid #000', padding: '4px 6px', color: '#fff', 'background-color': '#222', opacity: 0.90 }); edx.HtmlUtils.setHtml( $tooltip_div, edx.HtmlUtils.HTML(contents) ); edx.HtmlUtils.append( $('body'), edx.HtmlUtils.HTML($tooltip_div) ); $('#tooltip').fadeIn(200); } /* -------------------------------- Grade detail bars -------------------------------- */ <% colors = ["#b72121", "#600101", "#666666", "#333333"] categories = {} tickIndex = 1 sectionSpacer = 0.25 sectionIndex = 0 ticks = [] #These are the indices and x-axis labels for the data bottomTicks = [] #Labels on the bottom detail_tooltips = {} #This an dictionary mapping from 'section' -> array of detail_tooltips droppedScores = [] #These are the datapoints to indicate assignments which are not factored into the total score dropped_score_tooltips = [] for section in grade_summary['section_breakdown']: if section.get('prominent', False): tickIndex += sectionSpacer if section['category'] not in categories: colorIndex = len(categories) % len(colors) categories[ section['category'] ] = {'label' : section['category'], 'data' : [], 'color' : colors[colorIndex]} categoryData = categories[ section['category'] ] ## Because this is Python (Mako) embedded in JavaScript, our xss linting script is ## thoroughly confused. We should rewrite this file to remove Python/Mako. ## xss-lint: disable=javascript-jquery-append categoryData['data'].append( [tickIndex, section['percent']] ) ## Note that some courses had stored images in the Abbreviation. We are no longer ## allowing the display of such images, and remove any previously stored HTML ## to prevent ugly HTML from being shown to learners. ## xss-lint: disable=javascript-jquery-append ticks.append( [tickIndex, nh3.clean(section['label'], tags=set())] ) if section['category'] in detail_tooltips: ## xss-lint: disable=javascript-jquery-append detail_tooltips[ section['category'] ].append( section['detail'] ) else: detail_tooltips[ section['category'] ] = [ section['detail'], ] if 'mark' in section: ## xss-lint: disable=javascript-jquery-append droppedScores.append( [tickIndex, 0.05] ) ## xss-lint: disable=javascript-jquery-append dropped_score_tooltips.append( section['mark']['detail'] ) tickIndex += 1 if section.get('prominent', False): tickIndex += sectionSpacer ## ----------------------------- Grade overview bar ------------------------- ## tickIndex += sectionSpacer series = list(categories.values()) overviewBarX = tickIndex extraColorIndex = len(categories) #Keeping track of the next color to use for categories not in categories[] if show_grade_breakdown: for section in six.itervalues(grade_summary['grade_breakdown']): if section['percent'] > 0: if section['category'] in categories: color = categories[ section['category'] ]['color'] else: color = colors[ extraColorIndex % len(colors) ] extraColorIndex += 1 ## xss-lint: disable=javascript-jquery-append series.append({ 'label' : section['category'] + "-grade_breakdown", 'data' : [ [overviewBarX, section['percent']] ], 'color' : color }) detail_tooltips[section['category'] + "-grade_breakdown"] = [ section['detail'] ] ticks += [ [overviewBarX, "Total"] ] tickIndex += 1 + sectionSpacer totalScore = grade_summary['percent'] detail_tooltips['Dropped Scores'] = dropped_score_tooltips ## ----------------------------- Grade cutoffs ------------------------- ## grade_cutoff_ticks = [ [1, "100%"], [0, "0%"] ] if show_grade_cutoffs: grade_cutoff_ticks = [ [1, "100%"], [0, "0%"] ] descending_grades = sorted(grade_cutoffs, key=lambda x: grade_cutoffs[x], reverse=True) for grade in descending_grades: percent = grade_cutoffs[grade] ## xss-lint: disable=javascript-jquery-append grade_cutoff_ticks.append( [ percent, u"{0} {1:.0%}".format(grade, percent) ] ) else: grade_cutoff_ticks = [ ] %> var series = ${ series | n, dump_js_escaped_json }; var ticks = ${ ticks | n, dump_js_escaped_json }; var bottomTicks = ${ bottomTicks | n, dump_js_escaped_json }; var detail_tooltips = ${ detail_tooltips | n, dump_js_escaped_json }; var droppedScores = ${ droppedScores | n, dump_js_escaped_json }; var grade_cutoff_ticks = ${ grade_cutoff_ticks | n, dump_js_escaped_json } var yAxisTooltips={}; /* series looks like: [ { color: "#600101", label: "Homework", data: [[1, 0.06666666666666667], [2, 1], [3.25, .53]] }, ... ] detail_tooltips looks like: { "Dropped Scores": [0: "The lowest 1...:], "Homework": [ 0: "Homework 1 -- Homework -- Question Styles 7% (1/15)", 1: "Homework 2 -- Homework -- Get Social 100% (1/1)", 2: "Homework Average = 53%" ], ... } */ // loop through the series and extract the matching tick and the series label for (var seriesIndex = 0; seriesIndex < series.length; seriesIndex++) { for (var dataIndex = 0; dataIndex < series[seriesIndex]['data'].length; dataIndex++) { var tickIndex = series[seriesIndex]['data'][dataIndex][0]; // There may be more than one detail tooltip for a given tickIndex. If so, // push the new tooltip on the existing list. if (tickIndex in yAxisTooltips) { yAxisTooltips[tickIndex].push(detail_tooltips[series[seriesIndex]['label']][dataIndex]); } else { yAxisTooltips[tickIndex] = [detail_tooltips[series[seriesIndex]['label']][dataIndex]]; } // If this item was a dropped score, add the tooltip message about that. for (var droppedIndex = 0; droppedIndex < droppedScores.length; droppedIndex++) { if (tickIndex === droppedScores[droppedIndex][0]) { yAxisTooltips[tickIndex].push(detail_tooltips["Dropped Scores"][droppedIndex]); } } } } // hide the vertical axis since they are audibly lacking context for (var i = 0; i < grade_cutoff_ticks.length; i++) { grade_cutoff_ticks[i][1] = edx.HtmlUtils.joinHtml( edx.HtmlUtils.HTML('') ).text; } //Always be sure that one series has the xaxis set to 2, or the second xaxis labels won't show up series.push( {label: 'Dropped Scores', data: droppedScores, points: {symbol: "cross", show: true, radius: 3}, bars: {show: false}, color: "#333"} ); // Allow for arbitrary grade markers e.g. ['A', 'B', 'C'], ['Pass'], etc. var ascending_grades = grade_cutoff_ticks.map(function (el) { return el[0]; }); // Percentage point (in decimal) of each grade cutoff ascending_grades.sort(); // var colors = ['$gray-100', '$gray-200', '$gray-300']; var colors = ['#f8f9fa', '#e9ecef', '#dee2e6']; var markings = []; for(var i=1; i' : ''), ticks[i][1], edx.HtmlUtils.HTML('') ); var elementTooltips = yAxisTooltips[ticks[i][0]]; if (elementTooltips) { for (var tooltipIndex = 0; tooltipIndex < elementTooltips.length; tooltipIndex++) { tickLabel = edx.HtmlUtils.joinHtml( tickLabel, edx.HtmlUtils.HTML(''), elementTooltips[tooltipIndex], edx.HtmlUtils.HTML('
') ); } } ticks[i][1] = tickLabel; } return ticks; }, labelAngle: 90 }, yaxis: { ticks: grade_cutoff_ticks, min: 0.0, max: 1.0, labelWidth: 100 }, grid: { hoverable: true, clickable: true, borderWidth: 1, borderColor: "#707070", // gray-500 markings: markings }, legend: { show: false }, }; var $grade_detail_graph = $("#${graph_div_id | n, js_escaped_string}"); $grade_detail_graph.width($grade_detail_graph.parent().width()); if ($grade_detail_graph.length > 0) { var plot = $.plot($grade_detail_graph, series, options); %if show_grade_breakdown: var o = plot.pointOffset( {x: ${overviewBarX | n, dump_js_escaped_json} , y: ${totalScore | n, dump_js_escaped_json}} ); edx.HtmlUtils.append( $grade_detail_graph, edx.HtmlUtils.joinHtml( // xss-lint: disable=javascript-concat-html edx.HtmlUtils.HTML('
'), edx.HtmlUtils.HTML(''), gettext('Overall Score'), edx.HtmlUtils.HTML('
'), '${'{totalscore:.0%}'.format(totalscore=totalScore) | n, js_escaped_string}', edx.HtmlUtils.HTML('
') ) ); %endif $grade_detail_graph.find('.xAxis .tickLabel').attr('tabindex', '0').focus(function(event) { var $target = $(event.target), srElements = $target.find('.sr'), srText="", i; if (srElements.length > 0) { for (i = 0; i < srElements.length; i++) { srText += srElements[i].innerHTML; } // Position the tooltip slightly above the tick label. showTooltip($target.offset().left - 70, $target.offset().top - 120, srText); } }); $grade_detail_graph.focusout(function(){ $("#tooltip").remove(); }); } var previousPoint = null; $grade_detail_graph.bind("plothover", function (event, pos, item) { if (item) { if (previousPoint != (item.dataIndex, item.seriesIndex)) { previousPoint = (item.dataIndex, item.seriesIndex); if (item.series.label in detail_tooltips) { var series_tooltips = detail_tooltips[item.series.label]; if (item.dataIndex < series_tooltips.length) { showTooltip(item.pageX, item.pageY, series_tooltips[item.dataIndex]); } } } } else { $("#tooltip").remove(); previousPoint = null; } }); });