Commit 23c961d9 by Miles Steele

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

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