fix permissions, fix ENABLE_INSTRUCTOR_BACKGROUND_TASKS usage, fix csv download
This commit is contained in:
@@ -3,7 +3,6 @@ Instructor Dashboard API views
|
||||
|
||||
Non-html views which the instructor dashboard requests.
|
||||
|
||||
TODO add tracking
|
||||
TODO a lot of these GETs should be PUTs
|
||||
"""
|
||||
|
||||
@@ -14,6 +13,7 @@ from django.views.decorators.cache import cache_control
|
||||
from django.core.urlresolvers import reverse
|
||||
from django.http import HttpResponse, HttpResponseBadRequest
|
||||
|
||||
from courseware.access import has_access
|
||||
from courseware.courses import get_course_with_access
|
||||
from django.contrib.auth.models import User
|
||||
from django_comment_common.models import (Role,
|
||||
@@ -32,7 +32,10 @@ import analytics.csvs
|
||||
|
||||
|
||||
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):
|
||||
try:
|
||||
return func(*args, **kwargs)
|
||||
@@ -44,6 +47,8 @@ def common_exceptions_400(func):
|
||||
def require_query_params(*args, **kwargs):
|
||||
"""
|
||||
Checks for required paremters or renders a 400 error.
|
||||
(decorator with arguments)
|
||||
|
||||
`args` is a *list of required GET parameter names.
|
||||
`kwargs` is a **dict of required GET parameter names
|
||||
to string explanations of the parameter
|
||||
@@ -193,12 +198,12 @@ def access_allow_revoke(request, course_id):
|
||||
def list_course_role_members(request, course_id):
|
||||
"""
|
||||
List instructors and staff.
|
||||
Requires staff access.
|
||||
Requires instructor access.
|
||||
|
||||
rolename is one of ['instructor', 'staff', 'beta']
|
||||
"""
|
||||
course = get_course_with_access(
|
||||
request.user, course_id, 'staff', depth=None
|
||||
request.user, course_id, 'instructor', depth=None
|
||||
)
|
||||
|
||||
rolename = request.GET.get('rolename')
|
||||
@@ -232,7 +237,7 @@ def grading_config(request, course_id):
|
||||
"""
|
||||
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(
|
||||
request.user, course_id, 'staff', depth=None
|
||||
@@ -282,7 +287,7 @@ def enrolled_students_features(request, course_id, csv=False):
|
||||
)
|
||||
return response
|
||||
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)
|
||||
|
||||
|
||||
@@ -375,49 +380,19 @@ def get_student_progress_url(request, course_id):
|
||||
return response
|
||||
|
||||
|
||||
@ensure_csrf_cookie
|
||||
@cache_control(no_cache=True, no_store=True, must_revalidate=True)
|
||||
@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):
|
||||
"""
|
||||
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
|
||||
- problem_to_reset is a urlname of a problem
|
||||
- student_email is an email
|
||||
- all_students is a boolean
|
||||
- delete_module is a boolean
|
||||
- all_students is a boolean (requires instructor access) (mutually exclusive with delete_module)
|
||||
- delete_module is a boolean (requires instructor access) (mutually exclusive with all_students)
|
||||
"""
|
||||
course = get_course_with_access(
|
||||
request.user, course_id, 'staff', depth=None
|
||||
@@ -426,13 +401,18 @@ def reset_student_attempts(request, course_id):
|
||||
problem_to_reset = request.GET.get('problem_to_reset')
|
||||
student_email = request.GET.get('student_email')
|
||||
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)):
|
||||
return HttpResponseBadRequest()
|
||||
if will_delete_module and all_students:
|
||||
if delete_module and all_students:
|
||||
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)
|
||||
|
||||
response_payload = {}
|
||||
@@ -441,7 +421,7 @@ def reset_student_attempts(request, course_id):
|
||||
if 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)
|
||||
enrollment.reset_student_attempts(course_id, student, module_state_key, delete_module=delete_module)
|
||||
except StudentModule.DoesNotExist:
|
||||
return HttpResponseBadRequest("Module does not exist.")
|
||||
elif all_students:
|
||||
@@ -462,7 +442,7 @@ def reset_student_attempts(request, course_id):
|
||||
def rescore_problem(request, course_id):
|
||||
"""
|
||||
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
|
||||
- problem_to_reset is a urlname of a problem
|
||||
@@ -472,7 +452,7 @@ def rescore_problem(request, course_id):
|
||||
all_students will be ignored if student_email is present
|
||||
"""
|
||||
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')
|
||||
|
||||
@@ -45,7 +45,7 @@ def instructor_dashboard_2(request, course_id):
|
||||
sections = [
|
||||
_section_course_info(course_id),
|
||||
_section_membership(course_id, access),
|
||||
_section_student_admin(course_id),
|
||||
_section_student_admin(course_id, access),
|
||||
_section_data_download(course_id),
|
||||
_section_analytics(course_id),
|
||||
]
|
||||
@@ -115,11 +115,12 @@ def _section_membership(course_id, access):
|
||||
return section_data
|
||||
|
||||
|
||||
def _section_student_admin(course_id):
|
||||
def _section_student_admin(course_id, access):
|
||||
""" Provide data for the corresponding dashboard section """
|
||||
section_data = {
|
||||
'section_key': 'student_admin',
|
||||
'section_display_name': 'Student Admin',
|
||||
'access': access,
|
||||
'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}),
|
||||
'reset_student_attempts_url': reverse('reset_student_attempts', kwargs={'course_id': course_id}),
|
||||
|
||||
@@ -144,6 +144,9 @@ MITX_FEATURES = {
|
||||
# Enable instructor dash to submit background tasks
|
||||
'ENABLE_INSTRUCTOR_BACKGROUND_TASKS': True,
|
||||
|
||||
# Enable instructor dash beta version link
|
||||
'ENABLE_INSTRUCTOR_BETA_DASHBOARD': False,
|
||||
|
||||
# Allow use of the hint managment instructor view.
|
||||
'ENABLE_HINTER_INSTRUCTOR_VIEW': False,
|
||||
|
||||
|
||||
@@ -272,6 +272,7 @@ class Membership
|
||||
|
||||
@$list_selector.change =>
|
||||
$opt = @$list_selector.children('option:selected')
|
||||
return unless $opt.length > 0
|
||||
for auth_list in @auth_lists
|
||||
auth_list.$container.removeClass 'active'
|
||||
auth_list = $opt.data('auth_list')
|
||||
|
||||
@@ -9,6 +9,14 @@ std_ajax_err = (handler) -> (jqXHR, textStatus, errorThrown) ->
|
||||
errorThrown: #{errorThrown}"""
|
||||
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) ->
|
||||
$table_tasks.empty()
|
||||
@@ -61,39 +69,32 @@ class StudentAdmin
|
||||
log "setting up instructor dashboard section - student admin"
|
||||
@$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
|
||||
# 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']"
|
||||
@$progress_link = find_and_assert @$section, "a.progress-link"
|
||||
@$btn_enroll = find_and_assert @$section, "input[name='enroll']"
|
||||
@$btn_unenroll = find_and_assert @$section, "input[name='unenroll']"
|
||||
@$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_delete_state_single = find_and_assert @$section, "input[name='delete-state-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']"
|
||||
@$table_task_history_single = find_and_assert @$section, ".task-history-single-table"
|
||||
@$btn_delete_state_single = @$section.find "input[name='delete-state-single']"
|
||||
@$btn_rescore_problem_single = @$section.find "input[name='rescore-problem-single']"
|
||||
@$btn_task_history_single = @$section.find "input[name='task-history-single']"
|
||||
@$table_task_history_single = @$section.find ".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']"
|
||||
@$btn_task_history_all = find_and_assert @$section, "input[name='task-history-all']"
|
||||
@$table_task_history_all = find_and_assert @$section, ".task-history-all-table"
|
||||
@$table_running_tasks = find_and_assert @$section, ".running-tasks-table"
|
||||
# course-specific level buttons
|
||||
@$field_problem_select_all = @$section.find "input[name='problem-select-all']"
|
||||
@$btn_reset_attempts_all = @$section.find "input[name='reset-attempts-all']"
|
||||
@$btn_rescore_problem_all = @$section.find "input[name='rescore-problem-all']"
|
||||
@$btn_task_history_all = @$section.find "input[name='task-history-all']"
|
||||
@$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_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
|
||||
@$progress_link.click (e) =>
|
||||
@@ -118,7 +119,7 @@ class StudentAdmin
|
||||
|
||||
$.ajax
|
||||
dataType: 'json'
|
||||
url: @$btn_unenroll.data 'endpoint'
|
||||
url: @$btn_enroll.data 'endpoint'
|
||||
data: send_data
|
||||
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}'."
|
||||
@@ -259,7 +260,8 @@ class StudentAdmin
|
||||
cb?.apply this, arguments
|
||||
|
||||
onClickTitle: ->
|
||||
@start_refresh_running_task_poll_loop()
|
||||
if @$table_running_tasks.length > 0
|
||||
@start_refresh_running_task_poll_loop()
|
||||
|
||||
# onExit: ->
|
||||
# clearInterval @reload_running_task_list_slot
|
||||
|
||||
@@ -169,14 +169,16 @@
|
||||
|
||||
|
||||
.instructor-dashboard-wrapper-2 section.idash-section#membership {
|
||||
$half_width: $baseline * 20;
|
||||
|
||||
.vert-left {
|
||||
float: left;
|
||||
width: 47%;
|
||||
width: $half_width;
|
||||
}
|
||||
|
||||
.vert-right {
|
||||
float: right;
|
||||
width: 47%;
|
||||
width: $half_width;
|
||||
}
|
||||
|
||||
select {
|
||||
@@ -197,7 +199,7 @@
|
||||
position: absolute;
|
||||
display: none;
|
||||
padding: $baseline;
|
||||
width: $baseline * 25;
|
||||
width: $half_width;
|
||||
border: 1px solid $light-gray;
|
||||
background-color: $white;
|
||||
|
||||
|
||||
@@ -3,7 +3,7 @@
|
||||
<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" spellcheck="false"></textarea>
|
||||
<textarea rows="6" cols="50" 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'] }" >
|
||||
@@ -20,41 +20,43 @@
|
||||
</div>
|
||||
|
||||
<div class="vert-right member-lists-management">
|
||||
<h2> Member List Management </h2>
|
||||
<h2> Administration List Management </h2>
|
||||
|
||||
<select id="member-lists-selector">
|
||||
<option> Getting available lists... </option>
|
||||
</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']:
|
||||
<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-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 Instructor Access">
|
||||
<input type="button" name="allow" value="Grant Staff Access">
|
||||
</div>
|
||||
<div class="request-response-error"></div>
|
||||
</div>
|
||||
|
||||
%if section_data['access']['instructor']:
|
||||
<div class="auth-list-container" data-rolename="instructor" data-display-name="Instructors">
|
||||
<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 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>
|
||||
%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>
|
||||
|
||||
%if section_data['access']['forum_admin']:
|
||||
<div class="auth-list-container" data-rolename="Administrator" data-display-name="Forum Admins">
|
||||
<div class="auth-list-table" data-endpoint="${ section_data['list_forum_members_url'] }"></div>
|
||||
|
||||
@@ -28,54 +28,63 @@
|
||||
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" 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>
|
||||
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>
|
||||
%if settings.MITX_FEATURES.get('ENABLE_INSTRUCTOR_BACKGROUND_TASKS') and section_data['access']['instructor']:
|
||||
<input type="button" name="rescore-problem-single" value="Rescore Student Submission" data-endpoint="${ section_data['rescore_problem_url'] }">
|
||||
%endif
|
||||
|
||||
<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>
|
||||
%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'] }">
|
||||
<div class="task-history-single-table"></div>
|
||||
%endif
|
||||
</div>
|
||||
|
||||
<hr>
|
||||
%if settings.MITX_FEATURES.get('ENABLE_INSTRUCTOR_BACKGROUND_TASKS') and section_data['access']['instructor']:
|
||||
<hr>
|
||||
|
||||
<div class="course-specific-container">
|
||||
<H2>Course-specific grade adjustment</h2>
|
||||
<div class="request-response-error"></div>
|
||||
<div class="course-specific-container">
|
||||
<H2>Course-specific grade adjustment</h2>
|
||||
<div class="request-response-error"></div>
|
||||
|
||||
<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" 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'] }">
|
||||
</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>
|
||||
<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" 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'] }">
|
||||
</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>
|
||||
<hr>
|
||||
|
||||
<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>
|
||||
<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>
|
||||
%endif
|
||||
|
||||
Reference in New Issue
Block a user