Commit e05d0f97 by Miles Steele

fix permissions, fix ENABLE_INSTRUCTOR_BACKGROUND_TASKS usage, fix csv download

parent c6d6698a
...@@ -3,7 +3,6 @@ Instructor Dashboard API views ...@@ -3,7 +3,6 @@ Instructor Dashboard API views
Non-html views which the instructor dashboard requests. Non-html views which the instructor dashboard requests.
TODO add tracking
TODO a lot of these GETs should be PUTs TODO a lot of these GETs should be PUTs
""" """
...@@ -14,6 +13,7 @@ from django.views.decorators.cache import cache_control ...@@ -14,6 +13,7 @@ from django.views.decorators.cache import cache_control
from django.core.urlresolvers import reverse from django.core.urlresolvers import reverse
from django.http import HttpResponse, HttpResponseBadRequest from django.http import HttpResponse, HttpResponseBadRequest
from courseware.access import has_access
from courseware.courses import get_course_with_access from courseware.courses import get_course_with_access
from django.contrib.auth.models import User from django.contrib.auth.models import User
from django_comment_common.models import (Role, from django_comment_common.models import (Role,
...@@ -32,7 +32,10 @@ import analytics.csvs ...@@ -32,7 +32,10 @@ import analytics.csvs
def common_exceptions_400(func): def common_exceptions_400(func):
""" Catches common exceptions and renders matching 400 errors. (decorator) """ """
Catches common exceptions and renders matching 400 errors.
(decorator without arguments)
"""
def wrapped(*args, **kwargs): def wrapped(*args, **kwargs):
try: try:
return func(*args, **kwargs) return func(*args, **kwargs)
...@@ -44,6 +47,8 @@ def common_exceptions_400(func): ...@@ -44,6 +47,8 @@ def common_exceptions_400(func):
def require_query_params(*args, **kwargs): def require_query_params(*args, **kwargs):
""" """
Checks for required paremters or renders a 400 error. Checks for required paremters or renders a 400 error.
(decorator with arguments)
`args` is a *list of required GET parameter names. `args` is a *list of required GET parameter names.
`kwargs` is a **dict of required GET parameter names `kwargs` is a **dict of required GET parameter names
to string explanations of the parameter to string explanations of the parameter
...@@ -193,12 +198,12 @@ def access_allow_revoke(request, course_id): ...@@ -193,12 +198,12 @@ def access_allow_revoke(request, course_id):
def list_course_role_members(request, course_id): def list_course_role_members(request, course_id):
""" """
List instructors and staff. List instructors and staff.
Requires staff access. Requires instructor access.
rolename is one of ['instructor', 'staff', 'beta'] rolename is one of ['instructor', 'staff', 'beta']
""" """
course = get_course_with_access( course = get_course_with_access(
request.user, course_id, 'staff', depth=None request.user, course_id, 'instructor', depth=None
) )
rolename = request.GET.get('rolename') rolename = request.GET.get('rolename')
...@@ -232,7 +237,7 @@ def grading_config(request, course_id): ...@@ -232,7 +237,7 @@ def grading_config(request, course_id):
""" """
Respond with json which contains a html formatted grade summary. Respond with json which contains a html formatted grade summary.
TODO maybe this shouldn't be html already TODO this shouldn't be html already
""" """
course = get_course_with_access( course = get_course_with_access(
request.user, course_id, 'staff', depth=None request.user, course_id, 'staff', depth=None
...@@ -282,7 +287,7 @@ def enrolled_students_features(request, course_id, csv=False): ...@@ -282,7 +287,7 @@ def enrolled_students_features(request, course_id, csv=False):
) )
return response return response
else: else:
header, datarows = analytics.csvs.format_dictlist(student_data) header, datarows = analytics.csvs.format_dictlist(student_data, query_features)
return analytics.csvs.create_csv_response("enrolled_profiles.csv", header, datarows) return analytics.csvs.create_csv_response("enrolled_profiles.csv", header, datarows)
...@@ -378,46 +383,16 @@ def get_student_progress_url(request, course_id): ...@@ -378,46 +383,16 @@ 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 @common_exceptions_400
@require_query_params(student_email="student email")
def redirect_to_student_progress(request, course_id):
"""
Redirects to the specified students progress page
Limited to staff access.
Takes query parameter student_email
"""
course = get_course_with_access(
request.user, course_id, 'staff', depth=None
)
student_email = request.GET.get('student_email')
user = User.objects.get(email=student_email)
progress_url = reverse('student_progress', kwargs={'course_id': course_id, 'student_id': user.id})
response_payload = {
'course_id': course_id,
'progress_url': progress_url,
}
response = HttpResponse(
json.dumps(response_payload), content_type="application/json"
)
return response
@ensure_csrf_cookie
@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.
Limited to staff access. Limited to staff access. Some sub-methods limited to instructor access.
Takes either of the following query paremeters Takes either of the following query paremeters
- problem_to_reset is a urlname of a problem - problem_to_reset is a urlname of a problem
- student_email is an email - student_email is an email
- all_students is a boolean - all_students is a boolean (requires instructor access) (mutually exclusive with delete_module)
- delete_module is a boolean - delete_module is a boolean (requires instructor access) (mutually exclusive with all_students)
""" """
course = get_course_with_access( course = get_course_with_access(
request.user, course_id, 'staff', depth=None request.user, course_id, 'staff', depth=None
...@@ -426,13 +401,18 @@ def reset_student_attempts(request, course_id): ...@@ -426,13 +401,18 @@ def reset_student_attempts(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') student_email = request.GET.get('student_email')
all_students = request.GET.get('all_students', False) in ['true', 'True', True] all_students = request.GET.get('all_students', False) in ['true', 'True', True]
will_delete_module = request.GET.get('delete_module', False) in ['true', 'True', True] delete_module = request.GET.get('delete_module', False) 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()
if will_delete_module and all_students: if delete_module and all_students:
return HttpResponseBadRequest() return HttpResponseBadRequest()
# require instructor access for some queries
if all_students or delete_module:
if not has_access(request.user, course, 'instructor'):
HttpResponseBadRequest("requires instructor accesss.")
module_state_key = _msk_from_problem_urlname(course_id, problem_to_reset) module_state_key = _msk_from_problem_urlname(course_id, problem_to_reset)
response_payload = {} response_payload = {}
...@@ -441,7 +421,7 @@ def reset_student_attempts(request, course_id): ...@@ -441,7 +421,7 @@ def reset_student_attempts(request, course_id):
if student_email: if student_email:
try: try:
student = User.objects.get(email=student_email) 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=delete_module)
except StudentModule.DoesNotExist: except StudentModule.DoesNotExist:
return HttpResponseBadRequest("Module does not exist.") return HttpResponseBadRequest("Module does not exist.")
elif all_students: elif all_students:
...@@ -462,7 +442,7 @@ def reset_student_attempts(request, course_id): ...@@ -462,7 +442,7 @@ def reset_student_attempts(request, course_id):
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.
Limited to staff access. Limited to instructor access.
Takes either of the following query paremeters Takes either of the following query paremeters
- problem_to_reset is a urlname of a problem - problem_to_reset is a urlname of a problem
...@@ -472,7 +452,7 @@ def rescore_problem(request, course_id): ...@@ -472,7 +452,7 @@ def rescore_problem(request, course_id):
all_students will be ignored if student_email is present all_students will be ignored if student_email is present
""" """
course = get_course_with_access( course = get_course_with_access(
request.user, course_id, 'staff', depth=None request.user, course_id, 'instructor', depth=None
) )
problem_to_reset = request.GET.get('problem_to_reset') problem_to_reset = request.GET.get('problem_to_reset')
......
...@@ -45,7 +45,7 @@ def instructor_dashboard_2(request, course_id): ...@@ -45,7 +45,7 @@ def instructor_dashboard_2(request, course_id):
sections = [ sections = [
_section_course_info(course_id), _section_course_info(course_id),
_section_membership(course_id, access), _section_membership(course_id, access),
_section_student_admin(course_id), _section_student_admin(course_id, access),
_section_data_download(course_id), _section_data_download(course_id),
_section_analytics(course_id), _section_analytics(course_id),
] ]
...@@ -115,11 +115,12 @@ def _section_membership(course_id, access): ...@@ -115,11 +115,12 @@ def _section_membership(course_id, access):
return section_data return section_data
def _section_student_admin(course_id): def _section_student_admin(course_id, access):
""" Provide data for the corresponding dashboard section """ """ Provide data for the corresponding dashboard section """
section_data = { section_data = {
'section_key': 'student_admin', 'section_key': 'student_admin',
'section_display_name': 'Student Admin', 'section_display_name': 'Student Admin',
'access': access,
'get_student_progress_url': reverse('get_student_progress_url', kwargs={'course_id': course_id}), 'get_student_progress_url': reverse('get_student_progress_url', kwargs={'course_id': course_id}),
'enrollment_url': reverse('students_update_enrollment', kwargs={'course_id': course_id}), 'enrollment_url': reverse('students_update_enrollment', kwargs={'course_id': course_id}),
'reset_student_attempts_url': reverse('reset_student_attempts', kwargs={'course_id': course_id}), 'reset_student_attempts_url': reverse('reset_student_attempts', kwargs={'course_id': course_id}),
......
...@@ -144,6 +144,9 @@ MITX_FEATURES = { ...@@ -144,6 +144,9 @@ MITX_FEATURES = {
# Enable instructor dash to submit background tasks # Enable instructor dash to submit background tasks
'ENABLE_INSTRUCTOR_BACKGROUND_TASKS': True, 'ENABLE_INSTRUCTOR_BACKGROUND_TASKS': True,
# Enable instructor dash beta version link
'ENABLE_INSTRUCTOR_BETA_DASHBOARD': False,
# Allow use of the hint managment instructor view. # Allow use of the hint managment instructor view.
'ENABLE_HINTER_INSTRUCTOR_VIEW': False, 'ENABLE_HINTER_INSTRUCTOR_VIEW': False,
......
...@@ -272,6 +272,7 @@ class Membership ...@@ -272,6 +272,7 @@ class Membership
@$list_selector.change => @$list_selector.change =>
$opt = @$list_selector.children('option:selected') $opt = @$list_selector.children('option:selected')
return unless $opt.length > 0
for auth_list in @auth_lists for auth_list in @auth_lists
auth_list.$container.removeClass 'active' auth_list.$container.removeClass 'active'
auth_list = $opt.data('auth_list') auth_list = $opt.data('auth_list')
......
...@@ -9,6 +9,14 @@ std_ajax_err = (handler) -> (jqXHR, textStatus, errorThrown) -> ...@@ -9,6 +9,14 @@ std_ajax_err = (handler) -> (jqXHR, textStatus, errorThrown) ->
errorThrown: #{errorThrown}""" errorThrown: #{errorThrown}"""
handler.apply this, arguments handler.apply this, arguments
# get jquery element and assert its existance
find_and_assert = ($root, selector) ->
item = $root.find selector
if item.length != 1
console.error "element selection failed for '#{selector}' resulted in length #{item.length}"
throw "Failed Element Selection"
else
item
create_task_list_table = ($table_tasks, tasks_data) -> create_task_list_table = ($table_tasks, tasks_data) ->
$table_tasks.empty() $table_tasks.empty()
...@@ -61,39 +69,32 @@ class StudentAdmin ...@@ -61,39 +69,32 @@ class StudentAdmin
log "setting up instructor dashboard section - student admin" log "setting up instructor dashboard section - student admin"
@$section.data 'wrapper', @ @$section.data 'wrapper', @
# get jquery element and assert its existance
# for debugging
find_and_assert = ($root, selector) ->
item = $root.find selector
if item.length != 1
console.error "element selection failed for '#{selector}' resulted in length #{item.length}"
throw "Failed Element Selection"
else
item
# collect buttons # collect buttons
# some buttons are optional because they can be flipped by the instructor task feature switch
@$field_student_select = find_and_assert @$section, "input[name='student-select']" @$field_student_select = find_and_assert @$section, "input[name='student-select']"
@$progress_link = find_and_assert @$section, "a.progress-link" @$progress_link = find_and_assert @$section, "a.progress-link"
@$btn_enroll = find_and_assert @$section, "input[name='enroll']" @$btn_enroll = find_and_assert @$section, "input[name='enroll']"
@$btn_unenroll = find_and_assert @$section, "input[name='unenroll']" @$btn_unenroll = find_and_assert @$section, "input[name='unenroll']"
@$field_problem_select_single = find_and_assert @$section, "input[name='problem-select-single']" @$field_problem_select_single = find_and_assert @$section, "input[name='problem-select-single']"
@$btn_reset_attempts_single = find_and_assert @$section, "input[name='reset-attempts-single']" @$btn_reset_attempts_single = find_and_assert @$section, "input[name='reset-attempts-single']"
@$btn_delete_state_single = find_and_assert @$section, "input[name='delete-state-single']" @$btn_delete_state_single = @$section.find "input[name='delete-state-single']"
@$btn_rescore_problem_single = find_and_assert @$section, "input[name='rescore-problem-single']" @$btn_rescore_problem_single = @$section.find "input[name='rescore-problem-single']"
@$btn_task_history_single = find_and_assert @$section, "input[name='task-history-single']" @$btn_task_history_single = @$section.find "input[name='task-history-single']"
@$table_task_history_single = find_and_assert @$section, ".task-history-single-table" @$table_task_history_single = @$section.find ".task-history-single-table"
@$field_problem_select_all = find_and_assert @$section, "input[name='problem-select-all']" # course-specific level buttons
@$btn_reset_attempts_all = find_and_assert @$section, "input[name='reset-attempts-all']" @$field_problem_select_all = @$section.find "input[name='problem-select-all']"
@$btn_rescore_problem_all = find_and_assert @$section, "input[name='rescore-problem-all']" @$btn_reset_attempts_all = @$section.find "input[name='reset-attempts-all']"
@$btn_task_history_all = find_and_assert @$section, "input[name='task-history-all']" @$btn_rescore_problem_all = @$section.find "input[name='rescore-problem-all']"
@$table_task_history_all = find_and_assert @$section, ".task-history-all-table" @$btn_task_history_all = @$section.find "input[name='task-history-all']"
@$table_running_tasks = find_and_assert @$section, ".running-tasks-table" @$table_task_history_all = @$section.find ".task-history-all-table"
@$table_running_tasks = @$section.find ".running-tasks-table"
@$request_response_error_single = find_and_assert @$section, ".student-specific-container .request-response-error" @$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" @$request_response_error_all = @$section.find ".course-specific-container .request-response-error"
@start_refresh_running_task_poll_loop() if @$table_running_tasks.length > 0
@start_refresh_running_task_poll_loop()
# go to student progress page # go to student progress page
@$progress_link.click (e) => @$progress_link.click (e) =>
...@@ -118,7 +119,7 @@ class StudentAdmin ...@@ -118,7 +119,7 @@ class StudentAdmin
$.ajax $.ajax
dataType: 'json' dataType: 'json'
url: @$btn_unenroll.data 'endpoint' url: @$btn_enroll.data 'endpoint'
data: send_data data: send_data
success: @clear_errors_then -> console.log "student #{send_data.emails} enrolled" 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}'." error: std_ajax_err => @$request_response_error_single.text "Error enrolling student '#{send_data.emails}'."
...@@ -259,7 +260,8 @@ class StudentAdmin ...@@ -259,7 +260,8 @@ class StudentAdmin
cb?.apply this, arguments cb?.apply this, arguments
onClickTitle: -> onClickTitle: ->
@start_refresh_running_task_poll_loop() if @$table_running_tasks.length > 0
@start_refresh_running_task_poll_loop()
# onExit: -> # onExit: ->
# clearInterval @reload_running_task_list_slot # clearInterval @reload_running_task_list_slot
......
...@@ -169,14 +169,16 @@ ...@@ -169,14 +169,16 @@
.instructor-dashboard-wrapper-2 section.idash-section#membership { .instructor-dashboard-wrapper-2 section.idash-section#membership {
$half_width: $baseline * 20;
.vert-left { .vert-left {
float: left; float: left;
width: 47%; width: $half_width;
} }
.vert-right { .vert-right {
float: right; float: right;
width: 47%; width: $half_width;
} }
select { select {
...@@ -197,7 +199,7 @@ ...@@ -197,7 +199,7 @@
position: absolute; position: absolute;
display: none; display: none;
padding: $baseline; padding: $baseline;
width: $baseline * 25; width: $half_width;
border: 1px solid $light-gray; border: 1px solid $light-gray;
background-color: $white; background-color: $white;
......
...@@ -3,7 +3,7 @@ ...@@ -3,7 +3,7 @@
<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" spellcheck="false"></textarea> <textarea rows="6" cols="50" 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'] }" >
...@@ -20,40 +20,42 @@ ...@@ -20,40 +20,42 @@
</div> </div>
<div class="vert-right member-lists-management"> <div class="vert-right member-lists-management">
<h2> Member List Management </h2> <h2> Administration List Management </h2>
<select id="member-lists-selector"> <select id="member-lists-selector">
<option> Getting available lists... </option> <option> Getting available lists... </option>
</select> </select>
<div class="auth-list-container" data-rolename="staff" data-display-name="Staff">
<div class="auth-list-table" data-endpoint="${ section_data['list_course_role_members_url'] }"></div>
<div class="auth-list-add" data-endpoint="${ section_data['access_allow_revoke_url'] }">
<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']: %if section_data['access']['instructor']:
<div class="auth-list-container" data-rolename="instructor" data-display-name="Instructors"> <div class="auth-list-container" data-rolename="staff" data-display-name="Staff">
<div class="auth-list-table" data-endpoint="${ section_data['list_course_role_members_url'] }"></div> <div class="auth-list-table" data-endpoint="${ section_data['list_course_role_members_url'] }"></div>
<div class="auth-list-add" data-endpoint="${ section_data['access_allow_revoke_url'] }"> <div class="auth-list-add" data-endpoint="${ section_data['access_allow_revoke_url'] }">
<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 Staff Access">
</div> </div>
<div class="request-response-error"></div> <div class="request-response-error"></div>
</div> </div>
%endif
<div class="auth-list-container" data-rolename="beta" data-display-name="Beta Testers"> %if section_data['access']['instructor']:
<div class="auth-list-table" data-endpoint="${ section_data['list_course_role_members_url'] }"></div> <div class="auth-list-container" data-rolename="instructor" data-display-name="Instructors">
<div class="auth-list-add" data-endpoint="${ section_data['access_allow_revoke_url'] }"> <div class="auth-list-table" data-endpoint="${ section_data['list_course_role_members_url'] }"></div>
<input type="text" name="email" placeholder="Enter Email" spellcheck="false"> <div class="auth-list-add" data-endpoint="${ section_data['access_allow_revoke_url'] }">
<input type="button" name="allow" value="Grant Beta Tester Access"> <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
<div class="auth-list-container" data-rolename="beta" data-display-name="Beta Testers">
<div class="auth-list-table" data-endpoint="${ section_data['list_course_role_members_url'] }"></div>
<div class="auth-list-add" data-endpoint="${ section_data['access_allow_revoke_url'] }">
<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> </div>
<div class="request-response-error"></div> %endif
</div>
%if section_data['access']['forum_admin']: %if section_data['access']['forum_admin']:
<div class="auth-list-container" data-rolename="Administrator" data-display-name="Forum Admins"> <div class="auth-list-container" data-rolename="Administrator" data-display-name="Forum Admins">
......
...@@ -28,54 +28,63 @@ ...@@ -28,54 +28,63 @@
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>
<input type="button" class="molly-guard" 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'] }">
%if section_data['access']['instructor']:
<p> You may also delete the entire state of a student for the specified module: </p>
<input type="button" class="molly-guard" name="delete-state-single" value="Delete Student State for Module" data-endpoint="${ section_data['reset_student_attempts_url'] }">
%endif
<p> %if settings.MITX_FEATURES.get('ENABLE_INSTRUCTOR_BACKGROUND_TASKS') and section_data['access']['instructor']:
Rescoring runs in the background, and status for active tasks will appear in a table below. <input type="button" name="rescore-problem-single" value="Rescore Student Submission" data-endpoint="${ section_data['rescore_problem_url'] }">
To see status for all tasks submitted for this course and student, click on this button: %endif
</p>
%if settings.MITX_FEATURES.get('ENABLE_INSTRUCTOR_BACKGROUND_TASKS') and section_data['access']['instructor']:
<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'] }"> <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>
%endif
</div> </div>
<hr> %if settings.MITX_FEATURES.get('ENABLE_INSTRUCTOR_BACKGROUND_TASKS') and section_data['access']['instructor']:
<hr>
<div class="course-specific-container"> <div class="course-specific-container">
<H2>Course-specific grade adjustment</h2> <H2>Course-specific grade adjustment</h2>
<div class="request-response-error"></div> <div class="request-response-error"></div>
<p> <p>
Specify a particular problem in the course here by its url: Specify a particular problem in the course here by its url:
<input type="text" name="problem-select-all" size="60"> <input type="text" name="problem-select-all" size="60">
</p> </p>
<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>
<p> <p>
Then select an action: Then select an action:
<input type="button" class="molly-guard" name="reset-attempts-all" value="Reset ALL students' attempts" data-endpoint="${ section_data['reset_student_attempts_url'] }"> <input type="button" class="molly-guard" name="reset-attempts-all" value="Reset ALL students' attempts" data-endpoint="${ section_data['reset_student_attempts_url'] }">
<input type="button" class="molly-guard" name="rescore-problem-all" value="Rescore ALL students' problem submissions" data-endpoint="${ section_data['rescore_problem_url'] }"> <input type="button" class="molly-guard" name="rescore-problem-all" value="Rescore ALL students' problem submissions" data-endpoint="${ section_data['rescore_problem_url'] }">
</p> </p>
<p> <p>
<p>These actions run in the background, and status for active tasks will appear in a table below. <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: To see status for all tasks submitted for this problem, click on this button:
</p> </p>
<input type="button" name="task-history-all" value="Show Background Task History for Problem" data-endpoint="${ section_data['list_instructor_tasks_url'] }"> <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> <div class="task-history-all-table"></div>
</p> </p>
</div> </div>
<hr> <hr>
<div class="running-tasks-container"> <div class="running-tasks-container">
<h2> Pending Instructor Tasks </h2> <h2> Pending Instructor Tasks </h2>
<div class="running-tasks-table" data-endpoint="${ section_data['list_instructor_tasks_url'] }"></div> <div class="running-tasks-table" data-endpoint="${ section_data['list_instructor_tasks_url'] }"></div>
</div> </div>
%endif
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