Commit 23c961d9 by Miles Steele

visual cleanup, page error presentation cleanup, factor out common exceptions

parent dd43d663
......@@ -30,6 +30,16 @@ import analytics.distributions
import analytics.csvs
def common_exceptions_400(fn):
""" Catches common exceptions and renders matching 400 errors. (decorator) """
def wrapped(*args, **kwargs):
try:
return fn(*args, **kwargs)
except User.DoesNotExist:
return HttpResponseBadRequest("User does not exist.")
return wrapped
@ensure_csrf_cookie
@cache_control(no_cache=True, no_store=True, must_revalidate=True)
def students_update_enrollment_email(request, course_id):
......@@ -44,9 +54,9 @@ def students_update_enrollment_email(request, course_id):
"""
course = get_course_with_access(request.user, course_id, 'staff', depth=None)
action = request.GET.get('action', '')
emails = split_input_list(request.GET.get('emails', ''))
auto_enroll = request.GET.get('auto_enroll', '') in ['true', 'True', True]
action = request.GET.get('action')
emails = split_input_list(request.GET.get('emails'))
auto_enroll = request.GET.get('auto_enroll') in ['true', 'True', True]
if action == 'enroll':
results = enroll_emails(course_id, emails, auto_enroll=auto_enroll)
......@@ -66,6 +76,7 @@ def students_update_enrollment_email(request, course_id):
@ensure_csrf_cookie
@cache_control(no_cache=True, no_store=True, must_revalidate=True)
@common_exceptions_400
def access_allow_revoke(request, course_id):
"""
Modify staff/instructor access.
......@@ -109,7 +120,7 @@ def list_course_role_members(request, course_id):
"""
course = get_course_with_access(request.user, course_id, 'staff', depth=None)
rolename = request.GET.get('rolename', '')
rolename = request.GET.get('rolename')
if not rolename in ['instructor', 'staff', 'beta']:
return HttpResponseBadRequest()
......@@ -265,6 +276,7 @@ def get_student_progress_url(request, course_id):
@ensure_csrf_cookie
@cache_control(no_cache=True, no_store=True, must_revalidate=True)
@common_exceptions_400
def redirect_to_student_progress(request, course_id):
"""
Redirects to the specified students progress page
......@@ -276,8 +288,8 @@ def redirect_to_student_progress(request, course_id):
student_email = request.GET.get('student_email')
if not student_email:
# TODO Is there a way to do a - say - 'raise Http400'?
return HttpResponseBadRequest()
return HttpResponseBadRequest("Must provide an email.")
user = User.objects.get(email=student_email)
progress_url = reverse('student_progress', kwargs={'course_id': course_id, 'student_id': user.id})
......@@ -292,6 +304,7 @@ def redirect_to_student_progress(request, course_id):
@ensure_csrf_cookie
@cache_control(no_cache=True, no_store=True, must_revalidate=True)
@common_exceptions_400
def reset_student_attempts(request, course_id):
"""
Resets a students attempts counter or starts a task to reset all students attempts counters. Optionally deletes student state for a problem.
......@@ -321,11 +334,11 @@ def reset_student_attempts(request, course_id):
response_payload['problem_to_reset'] = problem_to_reset
if student_email:
student = User.objects.get(email=student_email)
try:
student = User.objects.get(email=student_email)
enrollment.reset_student_attempts(course_id, student, module_state_key, delete_module=will_delete_module)
except StudentModule.DoesNotExist:
return HttpResponseBadRequest()
return HttpResponseBadRequest("Module does not exist.")
elif all_students:
task = instructor_task.api.submit_reset_problem_attempts_for_all_students(request, course_id, module_state_key)
response_payload['task'] = 'created'
......@@ -338,6 +351,7 @@ def reset_student_attempts(request, course_id):
@ensure_csrf_cookie
@cache_control(no_cache=True, no_store=True, must_revalidate=True)
@common_exceptions_400
def rescore_problem(request, course_id):
"""
Starts a background process a students attempts counter. Optionally deletes student state for a problem.
......@@ -354,7 +368,7 @@ def rescore_problem(request, course_id):
problem_to_reset = request.GET.get('problem_to_reset')
student_email = request.GET.get('student_email', False)
all_students = request.GET.get('all_students', '') in ['true', 'True', True]
all_students = request.GET.get('all_students') in ['true', 'True', True]
if not (problem_to_reset and (all_students or student_email)):
return HttpResponseBadRequest()
......@@ -430,7 +444,7 @@ def list_forum_members(request, course_id):
"""
course = get_course_with_access(request.user, course_id, 'staff', depth=None)
rolename = request.GET.get('rolename', '')
rolename = request.GET.get('rolename')
if not rolename in [FORUM_ROLE_ADMINISTRATOR, FORUM_ROLE_MODERATOR, FORUM_ROLE_COMMUNITY_TA]:
return HttpResponseBadRequest()
......@@ -459,6 +473,7 @@ def list_forum_members(request, course_id):
@ensure_csrf_cookie
@cache_control(no_cache=True, no_store=True, must_revalidate=True)
@common_exceptions_400
def update_forum_role_membership(request, course_id):
"""
Modify forum role access.
......@@ -470,9 +485,9 @@ def update_forum_role_membership(request, course_id):
"""
course = get_course_with_access(request.user, course_id, 'instructor', depth=None)
email = request.GET.get('email', '')
rolename = request.GET.get('rolename', '')
mode = request.GET.get('mode', '')
email = request.GET.get('email')
rolename = request.GET.get('rolename')
mode = request.GET.get('mode')
if not rolename in [access.FORUM_ROLE_ADMINISTRATOR, access.FORUM_ROLE_MODERATOR, access.FORUM_ROLE_COMMUNITY_TA]:
return HttpResponseBadRequest()
......@@ -480,8 +495,8 @@ def update_forum_role_membership(request, course_id):
try:
user = User.objects.get(email=email)
access.update_forum_role_membership(course_id, user, rolename, mode)
except User.DoesNotExist, Role.DoesNotExist:
return HttpResponseBadRequest()
except Role.DoesNotExist:
return HttpResponseBadRequest("Role does not exist.")
response_payload = {
'course_id': course_id,
......
log = -> console.log.apply console, arguments
plantTimeout = (ms, cb) -> setTimeout cb, ms
std_ajax_err = (handler) -> (jqXHR, textStatus, errorThrown) ->
console.warn """ajax error
textStatus: #{textStatus}
errorThrown: #{errorThrown}"""
handler.apply this, arguments
class Analytics
constructor: (@$section) ->
log "setting up instructor dashboard section - analytics"
$display = @$section.find('.distribution-display')
@$display_text = $display.find('.distribution-display-text')
@$display_graph = $display.find('.distribution-display-graph')
@$display_table = $display.find('.distribution-display-table')
@$distribution_select = @$section.find('select#distributions')
@$display = @$section.find '.distribution-display'
@$display_text = @$display.find '.distribution-display-text'
@$display_graph = @$display.find '.distribution-display-graph'
@$display_table = @$display.find '.distribution-display-table'
@$distribution_select = @$section.find 'select#distributions'
@$request_response_error = @$display.find '.request-response-error'
@populate_selector => @$distribution_select.change => @on_selector_change()
......@@ -19,10 +26,13 @@ class Analytics
@$display_text.empty()
@$display_graph.empty()
@$display_table.empty()
@$request_response_error.empty()
populate_selector: (cb) ->
@get_profile_distributions [], (data) =>
@get_profile_distributions [],
error: std_ajax_err => @$request_response_error.text "Error getting available distributions."
success: (data) =>
@$distribution_select.find('option').eq(0).text "-- Select Distribution --"
for feature in data.available_features
......@@ -44,52 +54,54 @@ class Analytics
@reset_display()
return unless feature
@get_profile_distributions [feature], (data) =>
feature_res = data.feature_results[feature]
# feature response format: {'error': 'optional error string', 'type': 'SOME_TYPE', 'data': [stuff]}
if feature_res.error
console.warn(feature_res.error)
@$display_text.text 'Error fetching data'
else
if feature_res.type is 'EASY_CHOICE'
# setup SlickGrid
options =
enableCellNavigation: true
enableColumnReorder: false
forceFitColumns: true
columns = [
id: feature
field: feature
name: feature
,
id: 'count'
field: 'count'
name: 'Count'
]
grid_data = _.map feature_res.data, (value, key) ->
datapoint = {}
datapoint[feature] = key
datapoint['count'] = value
datapoint
table_placeholder = $ '<div/>', class: 'slickgrid'
@$display_table.append table_placeholder
grid = new Slick.Grid(table_placeholder, grid_data, columns, options)
# grid.autosizeColumns()
else if feature is 'year_of_birth'
graph_placeholder = $ '<div/>', class: 'year-of-birth'
@$display_graph.append graph_placeholder
graph_data = _.map feature_res.data, (value, key) -> [parseInt(key), value]
$.plot graph_placeholder, [
data: graph_data
]
@get_profile_distributions [feature],
error: std_ajax_err => @$request_response_error.text "Error getting distribution for '#{feature}'."
success: (data) =>
feature_res = data.feature_results[feature]
# feature response format: {'error': 'optional error string', 'type': 'SOME_TYPE', 'data': [stuff]}
if feature_res.error
console.warn(feature_res.error)
@$display_text.text 'Error fetching data'
else
console.warn("don't know how to show #{feature_res.type}")
@$display_text.text 'Unavailable Metric\n' + JSON.stringify(feature_res)
if feature_res.type is 'EASY_CHOICE'
# setup SlickGrid
options =
enableCellNavigation: true
enableColumnReorder: false
forceFitColumns: true
columns = [
id: feature
field: feature
name: feature
,
id: 'count'
field: 'count'
name: 'Count'
]
grid_data = _.map feature_res.data, (value, key) ->
datapoint = {}
datapoint[feature] = key
datapoint['count'] = value
datapoint
table_placeholder = $ '<div/>', class: 'slickgrid'
@$display_table.append table_placeholder
grid = new Slick.Grid(table_placeholder, grid_data, columns, options)
# grid.autosizeColumns()
else if feature is 'year_of_birth'
graph_placeholder = $ '<div/>', class: 'year-of-birth'
@$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("don't know how to show #{feature_res.type}")
@$display_text.text 'Unavailable Metric\n' + JSON.stringify(feature_res)
# handler can be either a callback for success or a mapping e.g. {success: ->, error: ->, complete: ->}
......
log = -> console.log.apply console, arguments
plantTimeout = (ms, cb) -> setTimeout cb, ms
std_ajax_err = (handler) -> (jqXHR, textStatus, errorThrown) ->
console.warn """ajax error
textStatus: #{textStatus}
errorThrown: #{errorThrown}"""
handler.apply this, arguments
class DataDownload
constructor: (@$section) ->
log "setting up instructor dashboard section - data download"
$display = @$section.find('.data-display')
@$display_text = $display.find('.data-display-text')
@$display_table = $display.find('.data-display-table')
@$display = @$section.find '.data-display'
@$display_text = @$display.find '.data-display-text'
@$display_table = @$display.find '.data-display-table'
@$request_response_error = @$display.find '.request-response-error'
$list_studs_btn = @$section.find("input[name='list-profiles']'")
$list_studs_btn.click (e) =>
......@@ -18,34 +25,50 @@ class DataDownload
url += '/csv'
location.href = url
else
@reset_display()
$.getJSON url, (data) =>
# setup SlickGrid
options =
enableCellNavigation: true
enableColumnReorder: false
forceFitColumns: true
columns = ({id: feature, field: feature, name: feature} for feature in data.queried_features)
grid_data = data.students
$table_placeholder = $ '<div/>', class: 'slickgrid'
@$display_table.append $table_placeholder
grid = new Slick.Grid($table_placeholder, grid_data, columns, options)
# grid.autosizeColumns()
@clear_display()
@$display_table.text 'Loading...'
$.ajax
dataType: 'json'
url: url
error: std_ajax_err =>
@clear_display()
@$request_response_error.text "Error getting student list."
success: (data) =>
@clear_display()
# setup SlickGrid
options =
enableCellNavigation: true
enableColumnReorder: false
forceFitColumns: true
columns = ({id: feature, field: feature, name: feature} for feature in data.queried_features)
grid_data = data.students
$table_placeholder = $ '<div/>', class: 'slickgrid'
@$display_table.append $table_placeholder
grid = new Slick.Grid($table_placeholder, grid_data, columns, options)
# grid.autosizeColumns()
$grade_config_btn = @$section.find("input[name='dump-gradeconf']'")
$grade_config_btn.click (e) =>
log "fetching grading config"
url = $grade_config_btn.data('endpoint')
$.getJSON url, (data) =>
@reset_display()
@$display_text.html data['grading_config_summary']
$.ajax
dataType: 'json'
url: url
error: std_ajax_err =>
@clear_display()
@$request_response_error.text "Error getting grading configuration."
success: (data) =>
@clear_display()
@$display_text.html data['grading_config_summary']
reset_display: ->
clear_display: ->
@$display_text.empty()
@$display_table.empty()
@$request_response_error.empty()
# exports
......
log = -> console.log.apply console, arguments
plantTimeout = (ms, cb) -> setTimeout cb, ms
std_ajax_err = (handler) -> (jqXHR, textStatus, errorThrown) ->
console.warn """ajax error
textStatus: #{textStatus}
errorThrown: #{errorThrown}"""
handler.apply this, arguments
class BatchEnrollment
constructor: (@$container) ->
log "setting up instructor dashboard subsection - batch enrollment"
$emails_input = @$container.find("textarea[name='student-emails']'")
$btn_enroll = @$container.find("input[name='enroll']'")
$btn_unenroll = @$container.find("input[name='unenroll']'")
$checkbox_autoenroll = @$container.find("input[name='auto-enroll']'")
window.autoenroll = $checkbox_autoenroll
$task_response = @$container.find(".task-response")
$emails_input = @$container.find("textarea[name='student-emails']'")
$btn_enroll = @$container.find("input[name='enroll']'")
$btn_unenroll = @$container.find("input[name='unenroll']'")
$checkbox_autoenroll = @$container.find("input[name='auto-enroll']'")
$task_response = @$container.find(".request-response")
$request_response_error = @$container.find(".request-response-error")
$emails_input.click -> log 'click $emails_input'
$btn_enroll.click -> log 'click $btn_enroll'
......@@ -22,21 +28,37 @@ class BatchEnrollment
action: 'enroll'
emails: $emails_input.val()
auto_enroll: $checkbox_autoenroll.is(':checked')
$.getJSON $btn_enroll.data('endpoint'), send_data, (data) ->
log 'received response for enroll button', data
display_response(data)
$.ajax
dataType: 'json'
url: $btn_enroll.data 'endpoint'
data: send_data
success: (data) -> display_response(data)
error: std_ajax_err -> fail_with_error "Error enrolling/unenrolling students."
$btn_unenroll.click ->
send_data =
action: 'unenroll'
emails: $emails_input.val()
auto_enroll: $checkbox_autoenroll.is(':checked')
$.getJSON $btn_unenroll.data('endpoint'), send_data, (data) ->
log 'received response for unenroll button', data
display_response(data)
$.ajax
dataType: 'json'
url: $btn_unenroll.data 'endpoint'
data: send_data
success: (data) -> display_response(data)
error: std_ajax_err -> fail_with_error "Error enrolling/unenrolling students."
fail_with_error = (msg) ->
console.warn msg
$task_response.empty()
$request_response_error.empty()
$request_response_error.text msg
display_response = (data_from_server) ->
$task_response.empty()
$request_response_error.empty()
response_code_dict = _.extend {}, data_from_server.results
# response_code_dict e.g. {'code': ['email1', 'email2'], ...}
......@@ -79,7 +101,7 @@ class BatchEnrollment
for msg_symbol in message_ordering
# $task_response.text JSON.stringify(data)
msg_txt = msg_to_txt[msg_symbol]
task_res_section = $ '<div/>', class: 'task-res-section'
task_res_section = $ '<div/>', class: 'request-res-section'
task_res_section.append $ '<h3/>', text: msg_txt
email_list = $ '<ul/>'
task_res_section.append email_list
......@@ -106,21 +128,21 @@ class AuthList
constructor: (@$container, @rolename) ->
log "setting up instructor dashboard subsection - authlist management for #{@rolename}"
@$display_table = @$container.find('.auth-list-table')
@$add_section = @$container.find('.auth-list-add')
$allow_field = @$add_section.find("input[name='email']")
$allow_button = @$add_section.find("input[name='allow']")
@$display_table = @$container.find('.auth-list-table')
@$request_response_error = @$container.find('.request-response-error')
@$add_section = @$container.find('.auth-list-add')
@$allow_field = @$add_section.find("input[name='email']")
@$allow_button = @$add_section.find("input[name='allow']")
$allow_button.click =>
@access_change($allow_field.val(), @rolename, 'allow', @reload_auth_list)
$allow_field.val ''
@$allow_button.click =>
@access_change @$allow_field.val(), @rolename, 'allow', => @reload_auth_list()
@$allow_field.val ''
@reload_auth_list()
reload_auth_list: =>
list_endpoint = @$display_table.data 'endpoint'
$.getJSON list_endpoint, {rolename: @rolename}, (data) =>
reload_auth_list: ->
load_auth_list = (data) =>
@$request_response_error.empty()
@$display_table.empty()
options =
......@@ -164,7 +186,15 @@ class AuthList
grid.onClick.subscribe (e, args) =>
item = args.grid.getDataItem(args.row)
if args.cell is WHICH_CELL_IS_REVOKE
@access_change(item.email, @rolename, 'revoke', @reload_auth_list)
@access_change item.email, @rolename, 'revoke', => @reload_auth_list()
$.ajax
dataType: 'json'
url: @$display_table.data 'endpoint'
data: rolename: @rolename
success: load_auth_list
error: std_ajax_err => @$request_response_error.text "Error fetching list for '#{@rolename}'"
# slickgrid collapses when rendered in an invisible div
# use this method to reload the widget
......@@ -173,9 +203,15 @@ class AuthList
@reload_auth_list()
access_change: (email, rolename, mode, cb) ->
access_change_endpoint = @$add_section.data 'endpoint'
$.getJSON access_change_endpoint, {email: email, rolename: @rolename, mode: mode}, (data) ->
cb?(data)
$.ajax
dataType: 'json'
url: @$add_section.data 'endpoint'
data:
email: email
rolename: rolename
mode: mode
success: (data) -> cb?(data)
error: std_ajax_err => @$request_response_error.text "Error changing user's permissions."
class Membership
......
......@@ -82,6 +82,7 @@ class StudentAdmin
@$btn_rescore_problem_single = find_and_assert @$section, "input[name='rescore-problem-single']"
@$btn_task_history_single = find_and_assert @$section, "input[name='task-history-single']"
@$table_task_history_single = find_and_assert @$section, ".task-history-single-table"
@$field_problem_select_all = find_and_assert @$section, "input[name='problem-select-all']"
@$btn_reset_attempts_all = find_and_assert @$section, "input[name='reset-attempts-all']"
@$btn_rescore_problem_all = find_and_assert @$section, "input[name='rescore-problem-all']"
......@@ -89,6 +90,9 @@ class StudentAdmin
@$table_task_history_all = find_and_assert @$section, ".task-history-all-table"
@$table_running_tasks = find_and_assert @$section, ".running-tasks-table"
@$request_response_error_single = find_and_assert @$section, ".student-specific-container .request-response-error"
@$request_response_error_all = find_and_assert @$section, ".course-specific-container .request-response-error"
@start_refresh_running_task_poll_loop()
# go to student progress page
......@@ -100,10 +104,10 @@ class StudentAdmin
dataType: 'json'
url: @$progress_link.data 'endpoint'
data: student_email: email
success: (data) ->
success: @clear_errors_then (data) ->
log 'redirecting...'
window.location = data.progress_url
error: std_ajax_err -> console.warn 'error getting student progress url for ' + email
error: std_ajax_err => @$request_response_error_single.text "Error getting student progress url for '#{email}'."
# enroll student
@$btn_enroll.click =>
......@@ -116,8 +120,8 @@ class StudentAdmin
dataType: 'json'
url: @$btn_unenroll.data 'endpoint'
data: send_data
success: -> console.log "student #{send_data.emails} enrolled"
error: std_ajax_err -> console.warn 'error enrolling student'
success: @clear_errors_then -> console.log "student #{send_data.emails} enrolled"
error: std_ajax_err => @$request_response_error_single.text "Error enrolling student '#{send_data.emails}'."
# unenroll student
@$btn_unenroll.click =>
......@@ -130,8 +134,8 @@ class StudentAdmin
dataType: 'json'
url: @$btn_unenroll.data 'endpoint'
data: send_data
success: -> console.log "student #{send_data.emails} unenrolled"
error: std_ajax_err -> console.warn 'error unenrolling student'
success: @clear_errors_then -> console.log "student #{send_data.emails} unenrolled"
error: std_ajax_err => @$request_response_error_single.text "Error unenrolling student '#{send_data.emails}'."
# reset attempts for student on problem
@$btn_reset_attempts_single.click =>
......@@ -144,8 +148,8 @@ class StudentAdmin
dataType: 'json'
url: @$btn_reset_attempts_single.data 'endpoint'
data: send_data
success: -> log 'problem attempts reset'
error: std_ajax_err -> console.warn 'error resetting problem state'
success: @clear_errors_then -> log 'problem attempts reset'
error: std_ajax_err => @$request_response_error_single.text "Error resetting problem attempts."
# delete state for student on problem
@$btn_delete_state_single.click =>
......@@ -158,8 +162,8 @@ class StudentAdmin
dataType: 'json'
url: @$btn_delete_state_single.data 'endpoint'
data: send_data
success: -> log 'module state deleted'
error: std_ajax_err -> console.warn 'error deleting problem state'
success: @clear_errors_then -> log 'module state deleted'
error: std_ajax_err => @$request_response_error_single.text "Error deleting problem state."
# start task to rescore problem for student
@$btn_rescore_problem_single.click =>
......@@ -171,8 +175,8 @@ class StudentAdmin
dataType: 'json'
url: @$btn_rescore_problem_single.data 'endpoint'
data: send_data
success: -> log 'started rescore problem task'
error: std_ajax_err -> console.warn 'error starting rescore problem (single student) task'
success: @clear_errors_then -> log 'started rescore problem task'
error: std_ajax_err => @$request_response_error_single.text "Error starting a task to rescore student's problem."
# list task history for student+problem
@$btn_task_history_single.click =>
......@@ -180,16 +184,18 @@ class StudentAdmin
student_email: @$field_student_select.val()
problem_urlname: @$field_problem_select_single.val()
if not send_data.student_email then return
if not send_data.problem_urlname then return
if not send_data.student_email
return @$request_response_error_single.text "Enter a student email."
if not send_data.problem_urlname
return @$request_response_error_single.text "Enter a problem urlname."
$.ajax
dataType: 'json'
url: @$btn_task_history_single.data 'endpoint'
data: send_data
success: (data) =>
success: @clear_errors_then (data) =>
create_task_list_table @$table_task_history_single, data.tasks
error: std_ajax_err -> console.warn 'error listing task history for student+problem'
error: std_ajax_err => @$request_response_error_single.text "Error getting task history for student+problem"
# start task to reset attempts on problem for all students
@$btn_reset_attempts_all.click =>
......@@ -201,9 +207,8 @@ class StudentAdmin
dataType: 'json'
url: @$btn_reset_attempts_all.data 'endpoint'
data: send_data
success: -> log 'started reset attempts task'
error: std_ajax_err (jqXHR, textStatus, errorThrown) ->
console.warn "error starting reset attempts (all students) task"
success: @clear_errors_then -> log 'started reset attempts task'
error: std_ajax_err => @$request_response_error_all.text "Error starting a task to reset attempts for all students on this problem."
# start task to rescore problem for all students
@$btn_rescore_problem_all.click =>
......@@ -215,24 +220,24 @@ class StudentAdmin
dataType: 'json'
url: @$btn_rescore_problem_all.data 'endpoint'
data: send_data
success: -> log 'started rescore problem task'
error: std_ajax_err (jqXHR, textStatus, errorThrown) ->
console.warn "error starting rescore problem (all students) task"
success: @clear_errors_then -> log 'started rescore problem task'
error: std_ajax_err => @$request_response_error_all.text "Error starting a task to rescore this problem for all students."
# list task history for problem
@$btn_task_history_all.click =>
send_data =
problem_urlname: @$field_problem_select_all.val()
if not send_data.problem_urlname then return
if not send_data.problem_urlname
return @$request_response_error_all.text "Enter a problem urlname."
$.ajax
dataType: 'json'
url: @$btn_task_history_all.data 'endpoint'
data: send_data
success: (data) =>
success: @clear_errors_then (data) =>
create_task_list_table @$table_task_history_all, data.tasks
error: std_ajax_err -> console.warn 'error listing task history for student+problem'
error: std_ajax_err => @$request_response_error_all.text "Error listing task history for this student and problem."
reload_running_tasks_list: =>
......@@ -241,13 +246,19 @@ class StudentAdmin
dataType: 'json'
url: list_endpoint
success: (data) => create_task_list_table @$table_running_tasks, data.tasks
error: std_ajax_err -> console.warn "error listing all instructor tasks"
error: std_ajax_err => console.warn "error listing all instructor tasks"
start_refresh_running_task_poll_loop: ->
@reload_running_tasks_list()
if @$section.hasClass 'active-section'
plantTimeout 5000, => @start_refresh_running_task_poll_loop()
clear_errors_then: (cb) ->
@$request_response_error_single.empty()
@$request_response_error_all.empty()
->
cb?.apply this, arguments
onClickTitle: ->
@start_refresh_running_task_poll_loop()
......
......@@ -5,8 +5,9 @@
.olddash-button-wrapper {
position: absolute;
top: 2em;
right: 2em;
top: 17px;
right: 15px;
font-size: 11pt;
}
section.instructor-dashboard-content-2 {
......@@ -19,6 +20,12 @@
// border: 1px solid blue;
// }
.request-response-error {
color: red;
margin-top: 1em;
margin-bottom: 1em;
}
.slickgrid {
font-family: verdana,arial,sans-serif;
font-size:11px;
......@@ -43,6 +50,13 @@
padding-bottom: 0;
}
input {
// display: block;
margin-bottom: 1em;
line-height: 1.3em;
padding: 8px 17px 8px 17px;
}
.instructor_dash_glob_info {
text-align: right;
position: absolute;
......@@ -125,25 +139,6 @@
margin-bottom: 1em;
}
.auth-list-container {
display: none;
margin-bottom: 1.5em;
&.active {
display: block;
}
.auth-list-table {
.slickgrid {
height: 250px;
}
}
.auth-list-add {
margin-top: 0.5em;
}
}
.batch-enrollment {
textarea {
margin-top: 0.2em;
......@@ -153,7 +148,11 @@
width: 500px;
}
.task-res-section {
input {
margin-right: 5px;
}
.request-res-section {
margin-top: 1.5em;
h3 {
......@@ -171,6 +170,25 @@
}
}
}
.auth-list-container {
display: none;
margin-bottom: 1.5em;
&.active {
display: block;
}
.auth-list-table {
.slickgrid {
height: 250px;
}
}
.auth-list-add {
margin-top: 0.5em;
}
}
}
......
......@@ -8,4 +8,5 @@
<div class="distribution-display-text"></div>
<div class="distribution-display-graph"></div>
<div class="distribution-display-table"></div>
<div class="request-response-error"></div>
</div>
......@@ -13,4 +13,5 @@
<div class="data-display">
<div class="data-display-text"></div>
<div class="data-display-table"></div>
<div class="request-response-error"></div>
</div>
......@@ -30,11 +30,7 @@
<div class="olddash-button-wrapper"><a href="${ old_dashboard_url }"> Back to Standard Dashboard </a></div>
<section class="instructor-dashboard-content-2">
<h1>Instructor Dashboard</h1>
## <div class="instructor_dash_glob_info">
## <span id="djangopid">${djangopid}</span> |
## <span id="mitxver">${mitx_version}</span>
## </div>
## <h1>Instructor Dashboard</h1>
## links which are tied to idash-sections below.
## the links are acativated and handled in instructor_dashboard.coffee
......
......@@ -3,13 +3,14 @@
<div class="vert-left batch-enrollment">
<h2>Batch Enrollment</h2>
<p>Enter student emails separated by new lines or commas.</p>
<textarea rows="6" cols="70" name="student-emails" placeholder="Student Emails"></textarea>
<textarea rows="6" cols="70" name="student-emails" placeholder="Student Emails" spellcheck="false"></textarea>
<br>
<input type="button" name="enroll" value="Enroll" data-endpoint="${ section_data['enroll_button_url'] }" >
<input type="button" name="unenroll" value="Unenroll" data-endpoint="${ section_data['unenroll_button_url'] }" >
<input type="checkbox" name="auto-enroll" value="Auto-Enroll">
<input type="checkbox" name="auto-enroll" value="Auto-Enroll" style="margin-top: 1em;">
<label for="auto-enroll">Auto Enroll</label>
<div class="task-response"></div>
<div class="request-response"></div>
<div class="request-response-error"></div>
</div>
<div class="vert-right member-lists-management">
......@@ -25,6 +26,7 @@
<input type="text" name="email" placeholder="Enter Email" spellcheck="false">
<input type="button" name="allow" value="Grant Staff Access">
</div>
<div class="request-response-error"></div>
</div>
%if section_data['access']['instructor']:
......@@ -34,6 +36,7 @@
<input type="text" name="email" placeholder="Enter Email" spellcheck="false">
<input type="button" name="allow" value="Grant Instructor Access">
</div>
<div class="request-response-error"></div>
</div>
%endif
......@@ -43,6 +46,7 @@
<input type="text" name="email" placeholder="Enter Email" spellcheck="false">
<input type="button" name="allow" value="Grant Beta Tester Access">
</div>
<div class="request-response-error"></div>
</div>
%if section_data['access']['forum_admin']:
......@@ -52,6 +56,7 @@
<input type="text" name="email" placeholder="Enter Email" spellcheck="false">
<input type="button" name="allow" value="Grant Forum Admin">
</div>
<div class="request-response-error"></div>
</div>
<div class="auth-list-container" data-rolename="Moderator" data-display-name="Forum Moderators">
......@@ -60,6 +65,7 @@
<input type="text" name="email" placeholder="Enter Email" spellcheck="false">
<input type="button" name="allow" value="Grant Forum Moderator">
</div>
<div class="request-response-error"></div>
</div>
<div class="auth-list-container" data-rolename="Community TA" data-display-name="Forum Community TAs">
......@@ -68,6 +74,7 @@
<input type="text" name="email" placeholder="Enter Email" spellcheck="false">
<input type="button" name="allow" value="Grant Community TA">
</div>
<div class="request-response-error"></div>
</div>
%endif
......
<%page args="section_data"/>
<H2>Student-specific grade adjustment</h2>
<div class="student-specific-container">
<H2>Student-specific grade adjustment</h2>
<div class="request-response-error"></div>
<input type="text" name="student-select" placeholder="Student Email">
<br>
<input type="text" name="student-select" placeholder="Student Email">
<br>
<div class="progress-link-wrapper">
<a href="" class="progress-link" data-endpoint="${ section_data['get_student_progress_url'] }">Student Progress Page</a>
</div>
<div class="progress-link-wrapper">
<a href="" class="progress-link" data-endpoint="${ section_data['get_student_progress_url'] }">Student Progress Page</a>
</div>
<br>
<input type="button" name="enroll" value="Enroll" data-endpoint="${ section_data['enrollment_url'] }">
<input type="button" name="unenroll" value="Unenroll" data-endpoint="${ section_data['enrollment_url'] }">
## <select class="problems">
## <option>Getting problems...</option>
## </select>
<br>
<input type="button" name="enroll" value="Enroll" data-endpoint="${ section_data['enrollment_url'] }">
<input type="button" name="unenroll" value="Unenroll" data-endpoint="${ section_data['enrollment_url'] }">
## <select class="problems">
## <option>Getting problems...</option>
## </select>
<p> Specify a particular problem in the course here by its url: </p>
<input type="text" name="problem-select-single" placeholder="Problem urlname">
<p>
You may use just the "urlname" if a problem, or "modulename/urlname" if not.
(For example, if the location is <tt>i4x://university/course/problem/problemname</tt>,
then just provide the <tt>problemname</tt>.
If the location is <tt>i4x://university/course/notaproblem/someothername</tt>, then
provide <tt>notaproblem/someothername</tt>.)
</p>
<input type="button" name="reset-attempts-single" value="Reset Student Attempts" data-endpoint="${ section_data['reset_student_attempts_url'] }">
<p> You may also delete the entire state of a student for the specified module: </p>
<input type="button" name="delete-state-single" value="Delete Student State for Module" data-endpoint="${ section_data['reset_student_attempts_url'] }">
<input type="button" name="rescore-problem-single" value="Rescore Student Submission" data-endpoint="${ section_data['rescore_problem_url'] }">
<p> Specify a particular problem in the course here by its url: </p>
<input type="text" name="problem-select-single" placeholder="Problem urlname">
<p>
You may use just the "urlname" if a problem, or "modulename/urlname" if not.
(For example, if the location is <tt>i4x://university/course/problem/problemname</tt>,
then just provide the <tt>problemname</tt>.
If the location is <tt>i4x://university/course/notaproblem/someothername</tt>, then
provide <tt>notaproblem/someothername</tt>.)
</p>
<input type="button" name="reset-attempts-single" value="Reset Student Attempts" data-endpoint="${ section_data['reset_student_attempts_url'] }">
<p> You may also delete the entire state of a student for the specified module: </p>
<input type="button" name="delete-state-single" value="Delete Student State for Module" data-endpoint="${ section_data['reset_student_attempts_url'] }">
<input type="button" name="rescore-problem-single" value="Rescore Student Submission" data-endpoint="${ section_data['rescore_problem_url'] }">
<p>
Rescoring runs in the background, and status for active tasks will appear in a table below.
To see status for all tasks submitted for this course and student, click on this button:
</p>
<p>
Rescoring runs in the background, and status for active tasks will appear in a table below.
To see status for all tasks submitted for this course and student, click on this button:
</p>
<input type="button" name="task-history-single" value="Show Background Task History for Student" data-endpoint="${ section_data['list_instructor_tasks_url'] }">
<div class="task-history-single-table"></div>
<input type="button" name="task-history-single" value="Show Background Task History for Student" data-endpoint="${ section_data['list_instructor_tasks_url'] }">
<div class="task-history-single-table"></div>
</div>
<hr>
<div class="course-specific-container">
<H2>Course-specific grade adjustment</h2>
<div class="request-response-error"></div>
<H2>Course-specific grade adjustment</h2>
<p>
Specify a particular problem in the course here by its url:
<input type="text" name="problem-select-all" size="60">
</p>
<p>
You may use just the "urlname" if a problem, or "modulename/urlname" if not.
(For example, if the location is <tt>i4x://university/course/problem/problemname</tt>,
then just provide the <tt>problemname</tt>.
If the location is <tt>i4x://university/course/notaproblem/someothername</tt>, then
provide <tt>notaproblem/someothername</tt>.)
</p>
<p>
Then select an action:
<input type="button" name="reset-attempts-all" value="Reset ALL students' attempts" data-endpoint="${ section_data['reset_student_attempts_url'] }">
<input type="button" name="rescore-problem-all" value="Rescore ALL students' problem submissions" data-endpoint="${ section_data['rescore_problem_url'] }">
</p>
<p>
<p>These actions run in the background, and status for active tasks will appear in a table below.
To see status for all tasks submitted for this problem, click on this button:
</p>
<input type="button" name="task-history-all" value="Show Background Task History for Problem" data-endpoint="${ section_data['list_instructor_tasks_url'] }">
<div class="task-history-all-table"></div>
</p>
<p>
Specify a particular problem in the course here by its url:
<input type="text" name="problem-select-all" size="60">
</p>
<p>
You may use just the "urlname" if a problem, or "modulename/urlname" if not.
(For example, if the location is <tt>i4x://university/course/problem/problemname</tt>,
then just provide the <tt>problemname</tt>.
If the location is <tt>i4x://university/course/notaproblem/someothername</tt>, then
provide <tt>notaproblem/someothername</tt>.)
</p>
<p>
Then select an action:
<input type="button" name="reset-attempts-all" value="Reset ALL students' attempts" data-endpoint="${ section_data['reset_student_attempts_url'] }">
<input type="button" name="rescore-problem-all" value="Rescore ALL students' problem submissions" data-endpoint="${ section_data['rescore_problem_url'] }">
</p>
<p>
<p>These actions run in the background, and status for active tasks will appear in a table below.
To see status for all tasks submitted for this problem, click on this button:
</p>
<input type="button" name="task-history-all" value="Show Background Task History for Problem" data-endpoint="${ section_data['list_instructor_tasks_url'] }">
<div class="task-history-all-table"></div>
</p>
</div>
<hr>
<h2> Pending Instructor Tasks </h2>
<div class="running-tasks-table" data-endpoint="${ section_data['list_instructor_tasks_url'] }"></div>
<div class="running-tasks-container">
<h2> Pending Instructor Tasks </h2>
<div class="running-tasks-table" data-endpoint="${ section_data['list_instructor_tasks_url'] }"></div>
</div>
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