Commit 376fe0a5 by Sarina Canelake

Remove outdated Analytics scripts, code, and css

parent 4f2be41a
......@@ -54,11 +54,11 @@ class TestInstructorDashboard(ModuleStoreTestCase, LoginEnrollmentTestCase):
return 'Enrollment data is now available in <a href="http://example.com/courses/{}" ' \
'target="_blank">Example</a>.'.format(unicode(self.course.id))
def get_dashboard_demographic_message(self):
def get_dashboard_analytics_message(self):
"""
Returns expected dashboard demographic message with link to Insights.
"""
return 'Demographic data is now available in <a href="http://example.com/courses/{}" ' \
return 'For analytics about your course, go to <a href="http://example.com/courses/{}" ' \
'target="_blank">Example</a>.'.format(unicode(self.course.id))
def test_instructor_tab(self):
......@@ -157,38 +157,28 @@ class TestInstructorDashboard(ModuleStoreTestCase, LoginEnrollmentTestCase):
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):
def test_dashboard_analytics_tab_not_shown(self):
"""
Test enrollment demographic data is shown.
Test dashboard analytics tab isn't shown if insights isn't configured.
"""
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)
analytics_section = '<li class="nav-item"><a href="" data-section="instructor_analytics">Analytics</a></li>'
self.assertFalse(analytics_section 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):
def test_dashboard_analytics_points_at_insights(self):
"""
Test enrollment demographic dashboard message is shown and data is hidden.
Test analytics dashboard message is shown
"""
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)
analytics_section = '<li class="nav-item"><a href="" data-section="instructor_analytics">Analytics</a></li>'
self.assertTrue(analytics_section in response.content)
# link to dashboard shown
expected_message = self.get_dashboard_demographic_message()
expected_message = self.get_dashboard_analytics_message()
self.assertTrue(expected_message in response.content)
def add_course_to_user_cart(self, cart, course_key):
......
......@@ -1647,56 +1647,6 @@ def get_anon_ids(request, course_id): # pylint: disable=unused-argument
@ensure_csrf_cookie
@cache_control(no_cache=True, no_store=True, must_revalidate=True)
@require_level('staff')
def get_distribution(request, course_id):
"""
Respond with json of the distribution of students over selected features which have choices.
Ask for a feature through the `feature` query parameter.
If no `feature` is supplied, will return response with an
empty response['feature_results'] object.
A list of available will be available in the response['available_features']
"""
course_id = SlashSeparatedCourseKey.from_deprecated_string(course_id)
feature = request.GET.get('feature')
# alternate notations of None
if feature in (None, 'null', ''):
feature = None
else:
feature = str(feature)
available_features = instructor_analytics.distributions.AVAILABLE_PROFILE_FEATURES
# allow None so that requests for no feature can list available features
if feature not in available_features + (None,):
return HttpResponseBadRequest(strip_tags(
"feature '{}' not available.".format(feature)
))
response_payload = {
'course_id': course_id.to_deprecated_string(),
'queried_feature': feature,
'available_features': available_features,
'feature_display_names': instructor_analytics.distributions.DISPLAY_NAMES,
}
p_dist = None
if feature is not None:
p_dist = instructor_analytics.distributions.profile_distribution(course_id, feature)
response_payload['feature_results'] = {
'feature': p_dist.feature,
'feature_display_name': p_dist.feature_display_name,
'data': p_dist.data,
'type': p_dist.type,
}
if p_dist.type == 'EASY_CHOICE':
response_payload['feature_results']['choices_display_names'] = p_dist.choices_display_names
return JsonResponse(response_payload)
@ensure_csrf_cookie
@cache_control(no_cache=True, no_store=True, must_revalidate=True)
@common_exceptions_400
@require_level('staff')
@require_query_params(
......@@ -2361,62 +2311,6 @@ def update_forum_role_membership(request, course_id):
return JsonResponse(response_payload)
@ensure_csrf_cookie
@cache_control(no_cache=True, no_store=True, must_revalidate=True)
@require_level('staff')
@require_query_params(
aname="name of analytic to query",
)
@common_exceptions_400
def proxy_legacy_analytics(request, course_id):
"""
Proxies to the analytics cron job server.
`aname` is a query parameter specifying which analytic to query.
"""
course_id = SlashSeparatedCourseKey.from_deprecated_string(course_id)
analytics_name = request.GET.get('aname')
# abort if misconfigured
if not (hasattr(settings, 'ANALYTICS_SERVER_URL') and
hasattr(settings, 'ANALYTICS_API_KEY') and
settings.ANALYTICS_SERVER_URL and settings.ANALYTICS_API_KEY):
return HttpResponse("Analytics service not configured.", status=501)
url = "{}get?aname={}&course_id={}&apikey={}".format(
settings.ANALYTICS_SERVER_URL,
analytics_name,
urllib.quote(unicode(course_id)),
settings.ANALYTICS_API_KEY,
)
try:
res = requests.get(url)
except Exception: # pylint: disable=broad-except
log.exception(u"Error requesting from analytics server at %s", url)
return HttpResponse("Error requesting from analytics server.", status=500)
if res.status_code is 200:
payload = json.loads(res.content)
add_block_ids(payload)
content = json.dumps(payload)
# return the successful request content
return HttpResponse(content, content_type="application/json")
elif res.status_code is 404:
# forward the 404 and content
return HttpResponse(res.content, content_type="application/json", status=404)
else:
# 500 on all other unexpected status codes.
log.error(
u"Error fetching %s, code: %s, msg: %s",
url, res.status_code, res.content
)
return HttpResponse(
"Error from analytics server ({}).".format(res.status_code),
status=500
)
@require_POST
def get_user_invoice_preference(request, course_id): # pylint: disable=unused-argument
"""
......
......@@ -33,8 +33,6 @@ urlpatterns = patterns(
'instructor.views.api.sale_validation', name="sale_validation"),
url(r'^get_anon_ids$',
'instructor.views.api.get_anon_ids', name="get_anon_ids"),
url(r'^get_distribution$',
'instructor.views.api.get_distribution', name="get_distribution"),
url(r'^get_student_progress_url$',
'instructor.views.api.get_student_progress_url', name="get_student_progress_url"),
url(r'^reset_student_attempts$',
......@@ -71,8 +69,6 @@ urlpatterns = patterns(
'instructor.views.api.list_forum_members', name="list_forum_members"),
url(r'^update_forum_role_membership$',
'instructor.views.api.update_forum_role_membership', name="update_forum_role_membership"),
url(r'^proxy_legacy_analytics$',
'instructor.views.api.proxy_legacy_analytics', name="proxy_legacy_analytics"),
url(r'^send_email$',
'instructor.views.api.send_email', name="send_email"),
url(r'^change_due_date$', 'instructor.views.api.change_due_date',
......
......@@ -334,9 +334,6 @@ FEATURES = {
# and register for course.
'ALLOW_AUTOMATED_SIGNUPS': False,
# Display demographic data on the analytics tab in the instructor dashboard.
'DISPLAY_ANALYTICS_DEMOGRAPHICS': True,
# Enable display of enrollment counts in instructor dash, analytics section
'DISPLAY_ANALYTICS_ENROLLMENTS': True,
......
###
Analytics Section
imports from other modules.
wrap in (-> ... apply) to defer evaluation
such that the value can be defined later than this assignment (file load order).
###
plantTimeout = -> window.InstructorDashboard.util.plantTimeout.apply this, arguments
std_ajax_err = -> window.InstructorDashboard.util.std_ajax_err.apply this, arguments
class ProfileDistributionWidget
constructor: ({@$container, @feature, @title, @endpoint}) ->
# render template
template_params =
title: @title
feature: @feature
endpoint: @endpoint
template_html = $("#profile-distribution-widget-template").text()
@$container.html Mustache.render template_html, template_params
reset_display: ->
@$container.find('.display-errors').empty()
@$container.find('.display-text').empty()
@$container.find('.display-graph').empty()
@$container.find('.display-table').empty()
show_error: (msg) ->
@$container.find('.display-errors').text msg
# display data
load: ->
@reset_display()
@get_profile_distributions @feature,
error: std_ajax_err =>
`// Translators: "Distribution" refers to a grade distribution. This error message appears when there is an error getting the data on grade distribution.`
@show_error gettext("Error fetching distribution.")
success: (data) =>
feature_res = data.feature_results
if feature_res.type is 'EASY_CHOICE'
# display on SlickGrid
options =
enableCellNavigation: true
enableColumnReorder: false
forceFitColumns: true
columns = [
id: @feature
field: @feature
name: data.feature_display_names[@feature]
,
id: 'count'
field: 'count'
name: 'Count'
]
grid_data = _.map feature_res.data, (value, key) =>
datapoint = {}
datapoint[@feature] = feature_res.choices_display_names[key]
datapoint['count'] = value
datapoint
table_placeholder = $ '<div/>', class: 'slickgrid'
@$container.find('.display-table').append table_placeholder
grid = new Slick.Grid(table_placeholder, grid_data, columns, options)
else if feature_res.feature is 'year_of_birth'
graph_placeholder = $ '<div/>', class: 'graph-placeholder'
@$container.find('.display-graph').append graph_placeholder
graph_data = _.map feature_res.data, (value, key) -> [parseInt(key), value]
$.plot graph_placeholder, [
data: graph_data
]
else
console.warn("unable to show distribution #{feature_res.type}")
@show_error gettext('Unavailable metric display.')
# fetch distribution data from server.
# `handler` can be either a callback for success
# or a mapping e.g. {success: ->, error: ->, complete: ->}
get_profile_distributions: (feature, handler) ->
settings =
dataType: 'json'
url: @endpoint
data: feature: feature
if typeof handler is 'function'
_.extend settings, success: handler
else
_.extend settings, handler
$.ajax settings
class GradeDistributionDisplay
constructor: ({@$container, @endpoint}) ->
template_params = {}
template_html = $('#grade-distributions-widget-template').text()
@$container.html Mustache.render template_html, template_params
@$problem_selector = @$container.find '.problem-selector'
reset_display: ->
@$container.find('.display-errors').empty()
@$container.find('.display-text').empty()
@$container.find('.display-graph').empty()
show_error: (msg) ->
@$container.find('.display-errors').text msg
load: ->
@get_grade_distributions
error: std_ajax_err => @show_error gettext("Error fetching grade distributions.")
success: (data) =>
time_updated = gettext("Last Updated: <%= timestamp %>")
full_time_updated = _.template(time_updated, {timestamp: data.time})
@$container.find('.last-updated').text full_time_updated
# populate selector
@$problem_selector.empty()
for {module_id, block_id, grade_info} in data.data
label = block_id
label ?= module_id
@$problem_selector.append $ '<option/>',
text: label
data:
module_id: module_id
grade_info: grade_info
@$problem_selector.change =>
$opt = @$problem_selector.children('option:selected')
return unless $opt.length > 0
@reset_display()
@render_distribution
module_id: $opt.data 'module_id'
grade_info: $opt.data 'grade_info'
# one-time first selection of first list item.
@$problem_selector.change()
render_distribution: ({module_id, grade_info}) ->
$display_graph = @$container.find('.display-graph')
graph_data = grade_info.map ({grade, max_grade, num_students}) -> [grade, num_students]
total_students = _.reduce ([0].concat grade_info),
(accum, {grade, max_grade, num_students}) -> accum + num_students
msg = gettext("<%= num_students %> students scored.")
full_msg = _.template(msg, {num_students: total_students})
# show total students
@$container.find('.display-text').text full_msg
# render to graph
graph_placeholder = $ '<div/>', class: 'graph-placeholder'
$display_graph.append graph_placeholder
graph_data = graph_data
$.plot graph_placeholder, [
data: graph_data
bars: show: true
color: '#1d9dd9'
]
# `handler` can be either a callback for success
# or a mapping e.g. {success: ->, error: ->, complete: ->}
#
# the data passed to the success handler takes this form:
# {
# "aname": "ProblemGradeDistribution",
# "time": "2013-07-31T20:25:56+00:00",
# "course_id": "MITx/6.002x/2013_Spring",
# "options": {
# "course_id": "MITx/6.002x/2013_Spring",
# "_id": "6fudge2b49somedbid1e1",
# "data": [
# {
# "module_id": "i4x://MITx/6.002x/problem/Capacitors_and_Energy_Storage",
# "grade_info": [
# {
# "grade": 0.0,
# "max_grade": 100.0,
# "num_students": 3
# }, ... for each grade number between 0 and max_grade
# ],
# }
get_grade_distributions: (handler) ->
settings =
dataType: 'json'
url: @endpoint
data: aname: 'ProblemGradeDistribution'
if typeof handler is 'function'
_.extend settings, success: handler
else
_.extend settings, handler
$.ajax settings
# Analytics Section
class InstructorAnalytics
constructor: (@$section) ->
@$section.data 'wrapper', @
@$pd_containers = @$section.find '.profile-distribution-widget-container'
@$gd_containers = @$section.find '.grade-distributions-widget-container'
@pdws = _.map (@$pd_containers), (container) =>
new ProfileDistributionWidget
$container: $(container)
feature: $(container).data 'feature'
title: $(container).data 'title'
endpoint: $(container).data 'endpoint'
@gdws = _.map (@$gd_containers), (container) =>
new GradeDistributionDisplay
$container: $(container)
endpoint: $(container).data 'endpoint'
refresh: ->
for pdw in @pdws
pdw.load()
for gdw in @gdws
gdw.load()
onClickTitle: ->
@refresh()
# export for use
# create parent namespaces if they do not already exist.
_.defaults window, InstructorDashboard: {}
_.defaults window.InstructorDashboard, sections: {}
_.defaults window.InstructorDashboard.sections,
InstructorAnalytics: InstructorAnalytics
......@@ -1355,40 +1355,6 @@
}
}
.profile-distribution-widget {
margin-bottom: ($baseline * 2);
.display-graph .graph-placeholder {
width: 750px;
height: 250px;
}
.display-table {
.slickgrid {
height: 250px;
}
}
}
.grade-distributions-widget {
margin-bottom: $baseline * 2;
.last-updated {
line-height: 2.2em;
@include font-size(12);
}
.display-graph .graph-placeholder {
width: 750px;
height: 200px;
}
.display-text {
line-height: 2em;
}
}
input[name="subject"] {
width:600px;
}
......@@ -1875,39 +1841,6 @@ input[name="subject"] {
}
.profile-distribution-widget {
margin-bottom: ($baseline * 2);
.display-graph .graph-placeholder {
width: 750px;
height: 250px;
}
.display-table {
.slickgrid {
height: 250px;
}
}
}
.grade-distributions-widget {
margin-bottom: ($baseline * 2);
.last-updated {
line-height: 2.2em;
@include font-size(12);
}
.display-graph .graph-placeholder {
width: 750px;
height: 200px;
}
.display-text {
line-height: 2em;
}
}
input[name="subject"] {
width:600px;
}
......
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