Commit 44b4204a by Greg Price

Merge pull request #1155 from edx/sarina/ins-dash-student-admin-2

parents 6b5d12e4 3813e51c
...@@ -512,7 +512,7 @@ class TestInstructorAPILevelsDataDump(ModuleStoreTestCase, LoginEnrollmentTestCa ...@@ -512,7 +512,7 @@ class TestInstructorAPILevelsDataDump(ModuleStoreTestCase, LoginEnrollmentTestCa
def test_get_student_progress_url(self): def test_get_student_progress_url(self):
""" Test that progress_url is in the successful response. """ """ Test that progress_url is in the successful response. """
url = reverse('get_student_progress_url', kwargs={'course_id': self.course.id}) url = reverse('get_student_progress_url', kwargs={'course_id': self.course.id})
url += "?student_email={}".format( url += "?unique_student_identifier={}".format(
quote(self.students[0].email.encode("utf-8")) quote(self.students[0].email.encode("utf-8"))
) )
print url print url
...@@ -522,6 +522,19 @@ class TestInstructorAPILevelsDataDump(ModuleStoreTestCase, LoginEnrollmentTestCa ...@@ -522,6 +522,19 @@ class TestInstructorAPILevelsDataDump(ModuleStoreTestCase, LoginEnrollmentTestCa
res_json = json.loads(response.content) res_json = json.loads(response.content)
self.assertIn('progress_url', res_json) self.assertIn('progress_url', res_json)
def test_get_student_progress_url_from_uname(self):
""" Test that progress_url is in the successful response. """
url = reverse('get_student_progress_url', kwargs={'course_id': self.course.id})
url += "?unique_student_identifier={}".format(
quote(self.students[0].username.encode("utf-8"))
)
print url
response = self.client.get(url)
print response
self.assertEqual(response.status_code, 200)
res_json = json.loads(response.content)
self.assertIn('progress_url', res_json)
def test_get_student_progress_url_noparams(self): def test_get_student_progress_url_noparams(self):
""" Test that the endpoint 404's without the required query params. """ """ Test that the endpoint 404's without the required query params. """
url = reverse('get_student_progress_url', kwargs={'course_id': self.course.id}) url = reverse('get_student_progress_url', kwargs={'course_id': self.course.id})
...@@ -579,7 +592,7 @@ class TestInstructorAPIRegradeTask(ModuleStoreTestCase, LoginEnrollmentTestCase) ...@@ -579,7 +592,7 @@ class TestInstructorAPIRegradeTask(ModuleStoreTestCase, LoginEnrollmentTestCase)
url = reverse('reset_student_attempts', kwargs={'course_id': self.course.id}) url = reverse('reset_student_attempts', kwargs={'course_id': self.course.id})
response = self.client.get(url, { response = self.client.get(url, {
'problem_to_reset': self.problem_urlname, 'problem_to_reset': self.problem_urlname,
'student_email': self.student.email, 'unique_student_identifier': self.student.email,
}) })
print response.content print response.content
self.assertEqual(response.status_code, 200) self.assertEqual(response.status_code, 200)
...@@ -608,7 +621,7 @@ class TestInstructorAPIRegradeTask(ModuleStoreTestCase, LoginEnrollmentTestCase) ...@@ -608,7 +621,7 @@ class TestInstructorAPIRegradeTask(ModuleStoreTestCase, LoginEnrollmentTestCase)
url = reverse('reset_student_attempts', kwargs={'course_id': self.course.id}) url = reverse('reset_student_attempts', kwargs={'course_id': self.course.id})
response = self.client.get(url, { response = self.client.get(url, {
'problem_to_reset': 'robot-not-a-real-module', 'problem_to_reset': 'robot-not-a-real-module',
'student_email': self.student.email, 'unique_student_identifier': self.student.email,
}) })
print response.content print response.content
self.assertEqual(response.status_code, 400) self.assertEqual(response.status_code, 400)
...@@ -618,7 +631,7 @@ class TestInstructorAPIRegradeTask(ModuleStoreTestCase, LoginEnrollmentTestCase) ...@@ -618,7 +631,7 @@ class TestInstructorAPIRegradeTask(ModuleStoreTestCase, LoginEnrollmentTestCase)
url = reverse('reset_student_attempts', kwargs={'course_id': self.course.id}) url = reverse('reset_student_attempts', kwargs={'course_id': self.course.id})
response = self.client.get(url, { response = self.client.get(url, {
'problem_to_reset': self.problem_urlname, 'problem_to_reset': self.problem_urlname,
'student_email': self.student.email, 'unique_student_identifier': self.student.email,
'delete_module': True, 'delete_module': True,
}) })
print response.content print response.content
...@@ -634,11 +647,11 @@ class TestInstructorAPIRegradeTask(ModuleStoreTestCase, LoginEnrollmentTestCase) ...@@ -634,11 +647,11 @@ class TestInstructorAPIRegradeTask(ModuleStoreTestCase, LoginEnrollmentTestCase)
) )
def test_reset_student_attempts_nonsense(self): def test_reset_student_attempts_nonsense(self):
""" Test failure with both student_email and all_students. """ """ Test failure with both unique_student_identifier and all_students. """
url = reverse('reset_student_attempts', kwargs={'course_id': self.course.id}) url = reverse('reset_student_attempts', kwargs={'course_id': self.course.id})
response = self.client.get(url, { response = self.client.get(url, {
'problem_to_reset': self.problem_urlname, 'problem_to_reset': self.problem_urlname,
'student_email': self.student.email, 'unique_student_identifier': self.student.email,
'all_students': True, 'all_students': True,
}) })
print response.content print response.content
...@@ -650,7 +663,19 @@ class TestInstructorAPIRegradeTask(ModuleStoreTestCase, LoginEnrollmentTestCase) ...@@ -650,7 +663,19 @@ class TestInstructorAPIRegradeTask(ModuleStoreTestCase, LoginEnrollmentTestCase)
url = reverse('rescore_problem', kwargs={'course_id': self.course.id}) url = reverse('rescore_problem', kwargs={'course_id': self.course.id})
response = self.client.get(url, { response = self.client.get(url, {
'problem_to_reset': self.problem_urlname, 'problem_to_reset': self.problem_urlname,
'student_email': self.student.email, 'unique_student_identifier': self.student.email,
})
print response.content
self.assertEqual(response.status_code, 200)
self.assertTrue(act.called)
@patch.object(instructor_task.api, 'submit_rescore_problem_for_student')
def test_rescore_problem_single_from_uname(self, act):
""" Test rescoring of a single student. """
url = reverse('rescore_problem', kwargs={'course_id': self.course.id})
response = self.client.get(url, {
'problem_to_reset': self.problem_urlname,
'unique_student_identifier': self.student.username,
}) })
print response.content print response.content
self.assertEqual(response.status_code, 200) self.assertEqual(response.status_code, 200)
...@@ -747,7 +772,7 @@ class TestInstructorAPITaskLists(ModuleStoreTestCase, LoginEnrollmentTestCase): ...@@ -747,7 +772,7 @@ class TestInstructorAPITaskLists(ModuleStoreTestCase, LoginEnrollmentTestCase):
url = reverse('list_instructor_tasks', kwargs={'course_id': self.course.id}) url = reverse('list_instructor_tasks', kwargs={'course_id': self.course.id})
response = self.client.get(url, { response = self.client.get(url, {
'problem_urlname': self.problem_urlname, 'problem_urlname': self.problem_urlname,
'student_email': self.student.email, 'unique_student_identifier': self.student.email,
}) })
print response.content print response.content
self.assertEqual(response.status_code, 200) self.assertEqual(response.status_code, 200)
......
...@@ -33,7 +33,7 @@ import instructor_task.api ...@@ -33,7 +33,7 @@ import instructor_task.api
from instructor_task.api_helper import AlreadyRunningError from instructor_task.api_helper import AlreadyRunningError
import instructor.enrollment as enrollment import instructor.enrollment as enrollment
from instructor.enrollment import enroll_email, unenroll_email from instructor.enrollment import enroll_email, unenroll_email
from instructor.views.tools import strip_if_string from instructor.views.tools import strip_if_string, get_student_from_identifier
import instructor.access as access import instructor.access as access
import analytics.basic import analytics.basic
import analytics.distributions import analytics.distributions
...@@ -456,20 +456,19 @@ def get_distribution(request, course_id): ...@@ -456,20 +456,19 @@ def get_distribution(request, course_id):
@common_exceptions_400 @common_exceptions_400
@require_level('staff') @require_level('staff')
@require_query_params( @require_query_params(
student_email="email of student for whom to get progress url" unique_student_identifier="email or username of student for whom to get progress url"
) )
def get_student_progress_url(request, course_id): def get_student_progress_url(request, course_id):
""" """
Get the progress url of a student. Get the progress url of a student.
Limited to staff access. Limited to staff access.
Takes query paremeter student_email and if the student exists Takes query paremeter unique_student_identifier and if the student exists
returns e.g. { returns e.g. {
'progress_url': '/../...' 'progress_url': '/../...'
} }
""" """
student_email = strip_if_string(request.GET.get('student_email')) user = get_student_from_identifier(request.GET.get('unique_student_identifier'))
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})
...@@ -496,7 +495,7 @@ def reset_student_attempts(request, course_id): ...@@ -496,7 +495,7 @@ def reset_student_attempts(request, course_id):
Takes some of the following query paremeters Takes some 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 - unique_student_identifier is an email or username
- all_students is a boolean - all_students is a boolean
requires instructor access requires instructor access
mutually exclusive with delete_module mutually exclusive with delete_module
...@@ -510,14 +509,17 @@ def reset_student_attempts(request, course_id): ...@@ -510,14 +509,17 @@ def reset_student_attempts(request, course_id):
) )
problem_to_reset = strip_if_string(request.GET.get('problem_to_reset')) problem_to_reset = strip_if_string(request.GET.get('problem_to_reset'))
student_email = strip_if_string(request.GET.get('student_email')) student_identifier = request.GET.get('unique_student_identifier', None)
student = None
if student_identifier is not None:
student = get_student_from_identifier(student_identifier)
all_students = request.GET.get('all_students', False) in ['true', 'True', True] all_students = request.GET.get('all_students', False) in ['true', 'True', True]
delete_module = request.GET.get('delete_module', False) in ['true', 'True', True] delete_module = request.GET.get('delete_module', False) in ['true', 'True', True]
# parameter combinations # parameter combinations
if all_students and student_email: if all_students and student:
return HttpResponseBadRequest( return HttpResponseBadRequest(
"all_students and student_email are mutually exclusive." "all_students and unique_student_identifier are mutually exclusive."
) )
if all_students and delete_module: if all_students and delete_module:
return HttpResponseBadRequest( return HttpResponseBadRequest(
...@@ -534,15 +536,16 @@ def reset_student_attempts(request, course_id): ...@@ -534,15 +536,16 @@ def reset_student_attempts(request, course_id):
response_payload = {} response_payload = {}
response_payload['problem_to_reset'] = problem_to_reset response_payload['problem_to_reset'] = problem_to_reset
if student_email: if student:
try: try:
student = User.objects.get(email=student_email)
enrollment.reset_student_attempts(course_id, student, module_state_key, delete_module=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.")
response_payload['student'] = student_identifier
elif all_students: elif all_students:
instructor_task.api.submit_reset_problem_attempts_for_all_students(request, course_id, module_state_key) instructor_task.api.submit_reset_problem_attempts_for_all_students(request, course_id, module_state_key)
response_payload['task'] = 'created' response_payload['task'] = 'created'
response_payload['student'] = 'All Students'
else: else:
return HttpResponseBadRequest() return HttpResponseBadRequest()
...@@ -561,21 +564,25 @@ def rescore_problem(request, course_id): ...@@ -561,21 +564,25 @@ def rescore_problem(request, course_id):
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 - unique_student_identifier is an email or username
- all_students is a boolean - all_students is a boolean
all_students and student_email cannot both be present. all_students and unique_student_identifier cannot both be present.
""" """
problem_to_reset = strip_if_string(request.GET.get('problem_to_reset')) problem_to_reset = strip_if_string(request.GET.get('problem_to_reset'))
student_email = strip_if_string(request.GET.get('student_email', False)) student_identifier = request.GET.get('unique_student_identifier', None)
student = None
if student_identifier is not None:
student = get_student_from_identifier(student_identifier)
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)):
return HttpResponseBadRequest("Missing query parameters.") return HttpResponseBadRequest("Missing query parameters.")
if all_students and student_email: if all_students and student:
return HttpResponseBadRequest( return HttpResponseBadRequest(
"Cannot rescore with all_students and student_email." "Cannot rescore with all_students and unique_student_identifier."
) )
module_state_key = _msk_from_problem_urlname(course_id, problem_to_reset) module_state_key = _msk_from_problem_urlname(course_id, problem_to_reset)
...@@ -583,9 +590,8 @@ def rescore_problem(request, course_id): ...@@ -583,9 +590,8 @@ def rescore_problem(request, course_id):
response_payload = {} response_payload = {}
response_payload['problem_to_reset'] = problem_to_reset response_payload['problem_to_reset'] = problem_to_reset
if student_email: if student:
response_payload['student_email'] = student_email response_payload['student'] = student_identifier
student = User.objects.get(email=student_email)
instructor_task.api.submit_rescore_problem_for_student(request, course_id, module_state_key, student) instructor_task.api.submit_rescore_problem_for_student(request, course_id, module_state_key, student)
response_payload['task'] = 'created' response_payload['task'] = 'created'
elif all_students: elif all_students:
...@@ -608,21 +614,22 @@ def list_instructor_tasks(request, course_id): ...@@ -608,21 +614,22 @@ def list_instructor_tasks(request, course_id):
Takes optional query paremeters. Takes optional query paremeters.
- With no arguments, lists running tasks. - With no arguments, lists running tasks.
- `problem_urlname` lists task history for problem - `problem_urlname` lists task history for problem
- `problem_urlname` and `student_email` lists task - `problem_urlname` and `unique_student_identifier` lists task
history for problem AND student (intersection) history for problem AND student (intersection)
""" """
problem_urlname = strip_if_string(request.GET.get('problem_urlname', False)) problem_urlname = strip_if_string(request.GET.get('problem_urlname', False))
student_email = strip_if_string(request.GET.get('student_email', False)) student = request.GET.get('unique_student_identifier', None)
if student is not None:
student = get_student_from_identifier(student)
if student_email and not problem_urlname: if student and not problem_urlname:
return HttpResponseBadRequest( return HttpResponseBadRequest(
"student_email must accompany problem_urlname" "unique_student_identifier must accompany problem_urlname"
) )
if problem_urlname: if problem_urlname:
module_state_key = _msk_from_problem_urlname(course_id, problem_urlname) module_state_key = _msk_from_problem_urlname(course_id, problem_urlname)
if student_email: if student:
student = User.objects.get(email=student_email)
tasks = instructor_task.api.get_instructor_task_history(course_id, module_state_key, student) tasks = instructor_task.api.get_instructor_task_history(course_id, module_state_key, student)
else: else:
tasks = instructor_task.api.get_instructor_task_history(course_id, module_state_key) tasks = instructor_task.api.get_instructor_task_history(course_id, module_state_key)
......
...@@ -38,7 +38,7 @@ def instructor_dashboard_2(request, course_id): ...@@ -38,7 +38,7 @@ def instructor_dashboard_2(request, course_id):
raise Http404() raise Http404()
sections = [ sections = [
_section_course_info(course_id), _section_course_info(course_id, access),
_section_membership(course_id, access), _section_membership(course_id, access),
_section_student_admin(course_id, access), _section_student_admin(course_id, access),
_section_data_download(course_id), _section_data_download(course_id),
...@@ -67,18 +67,21 @@ section_display_name will be used to generate link titles in the nav bar. ...@@ -67,18 +67,21 @@ section_display_name will be used to generate link titles in the nav bar.
""" # pylint: disable=W0105 """ # pylint: disable=W0105
def _section_course_info(course_id): def _section_course_info(course_id, access):
""" Provide data for the corresponding dashboard section """ """ Provide data for the corresponding dashboard section """
course = get_course_by_id(course_id, depth=None) course = get_course_by_id(course_id, depth=None)
section_data = {} section_data = {
section_data['section_key'] = 'course_info' 'section_key': 'course_info',
section_data['section_display_name'] = _('Course Info') 'section_display_name': _('Course Info'),
section_data['course_id'] = course_id 'course_id': course_id,
section_data['course_display_name'] = course.display_name 'access': access,
section_data['enrollment_count'] = CourseEnrollment.objects.filter(course_id=course_id).count() 'course_display_name': course.display_name,
section_data['has_started'] = course.has_started() 'enrollment_count': CourseEnrollment.objects.filter(course_id=course_id).count(),
section_data['has_ended'] = course.has_ended() 'has_started': course.has_started(),
'has_ended': course.has_ended(),
'list_instructor_tasks_url': reverse('list_instructor_tasks', kwargs={'course_id': course_id}),
}
try: try:
advance = lambda memo, (letter, score): "{}: {}, ".format(letter, score) + memo advance = lambda memo, (letter, score): "{}: {}, ".format(letter, score) + memo
......
""" """
Tools for the instructor dashboard Tools for the instructor dashboard
""" """
from django.contrib.auth.models import User
def strip_if_string(value): def strip_if_string(value):
if isinstance(value, basestring): if isinstance(value, basestring):
return value.strip() return value.strip()
return value return value
def get_student_from_identifier(unique_student_identifier):
"""
Gets a student object using either an email address or username.
Returns the student object associated with `unique_student_identifier`
Raises User.DoesNotExist if no user object can be found.
"""
unique_student_identifier = strip_if_string(unique_student_identifier)
if "@" in unique_student_identifier:
student = User.objects.get(email=unique_student_identifier)
else:
student = User.objects.get(username=unique_student_identifier)
return student
...@@ -240,7 +240,7 @@ function goto( mode) ...@@ -240,7 +240,7 @@ function goto( mode)
<hr width="40%" style="align:left"> <hr width="40%" style="align:left">
%endif %endif
<H2>${_("Student-specific grade inspection and adjustment")}</h2> <h2>${_("Student-specific grade inspection and adjustment")}</h2>
<p> <p>
${_("Specify the {platform_name} email address or username of a student here:").format(platform_name=settings.PLATFORM_NAME)} ${_("Specify the {platform_name} email address or username of a student here:").format(platform_name=settings.PLATFORM_NAME)}
<input type="text" name="unique_student_identifier"> <input type="text" name="unique_student_identifier">
......
...@@ -38,8 +38,21 @@ ...@@ -38,8 +38,21 @@
## ${ section_data['offline_grades'] } ## ${ section_data['offline_grades'] }
## </div> ## </div>
%if settings.MITX_FEATURES.get('ENABLE_INSTRUCTOR_BACKGROUND_TASKS') and section_data['access']['instructor']:
<div class="running-tasks-container action-type-container">
<hr>
<h2> ${_("Pending Instructor Tasks")} </h2>
<p>${_("The status for any active tasks appears in a table below.")} </p>
<div class="running-tasks-table" data-endpoint="${ section_data['list_instructor_tasks_url'] }"></div>
</div>
%endif
%if len(section_data['course_errors']): %if len(section_data['course_errors']):
<div class="course-errors-wrapper"> <div class="course-errors-wrapper">
<hr>
<p>
<div class="toggle-wrapper"> <div class="toggle-wrapper">
<h2 class="title">${_("Course Warnings")}:</h2> <h2 class="title">${_("Course Warnings")}:</h2>
<div class="triangle"></div> <div class="triangle"></div>
...@@ -52,5 +65,10 @@ ...@@ -52,5 +65,10 @@
</div> </div>
%endfor %endfor
</div> </div>
<p>
</div> </div>
<br>
%endif %endif
...@@ -2,25 +2,49 @@ ...@@ -2,25 +2,49 @@
<%page args="section_data"/> <%page args="section_data"/>
<div class="student-specific-container action-type-container"> <div class="student-specific-container action-type-container">
<H2>${_("Student-specific grade adjustment")}</h2> <h2>${_("Student-specific grade inspection")}</h2>
<div class="request-response-error"></div> <div class="request-response-error"></div>
<p>
<input type="text" name="student-select" placeholder="${_("Student Email")}"> <!-- Doesn't work for username but this MUST work -->
${_("Specify the {platform_name} email address or username of a student here:").format(platform_name=settings.PLATFORM_NAME)}
<input type="text" name="student-select-progress" placeholder="${_("Student Email or Username")}">
</p>
<br> <br>
<div class="progress-link-wrapper"> <div class="progress-link-wrapper">
<p>
${_("Click this link to view the student's progress page:")}
<a href="" class="progress-link" data-endpoint="${ section_data['get_student_progress_url_url'] }"> ${_("Student Progress Page")} </a> <a href="" class="progress-link" data-endpoint="${ section_data['get_student_progress_url_url'] }"> ${_("Student Progress Page")} </a>
</p>
</div> </div>
<br> <br>
<!-- These buttons don't appear to be working
<p>
${_("Click to enroll or unenroll this student from the course:")}
<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"> </p>
## <option>Getting problems...</option> -->
## </select>
<p> ${_('Specify a particular problem in the course here by its url:')} </p> <hr>
</div>
<div class="student-grade-container action-type-container">
<h2>${_("Student-specific grade adjustment")}</h2>
<div class="request-response-error"></div>
<p>
<!-- Doesn't work for username but this MUST work -->
${_("Specify the {platform_name} email address or username of a student here:").format(platform_name=settings.PLATFORM_NAME)}
<input type="text" name="student-select-grade" placeholder="${_("Student Email or Username")}">
</p>
<br>
<p> ${_('Specify a particular problem in the course here by its url:')}
<input type="text" name="problem-select-single" placeholder="${_("Problem urlname")}"> <input type="text" name="problem-select-single" placeholder="${_("Problem urlname")}">
</p>
<p> <p>
${_('You may use just the "urlname" if a problem, or "modulename/urlname" if not. (For example, if the location is {location1}, then just provide the {urlname1}. If the location is {location2}, then provide {urlname2}.)').format( ${_('You may use just the "urlname" if a problem, or "modulename/urlname" if not. (For example, if the location is {location1}, then just provide the {urlname1}. If the location is {location2}, then provide {urlname2}.)').format(
location1="<tt>i4x://university/course/problem/problemname</tt>", location1="<tt>i4x://university/course/problem/problemname</tt>",
...@@ -29,20 +53,31 @@ ...@@ -29,20 +53,31 @@
urlname2="<tt>notaproblem/someothername</tt>") urlname2="<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'] }">
%if section_data['access']['instructor']: <p>
<p> ${_('You may also delete the entire state of a student for the specified module:')} </p> ${_("Next, select an action to perform for the given user and problem:")}
<input type="button" class="molly-guard" name="delete-state-single" value="${_("Delete Student State for Module")}" data-endpoint="${ section_data['reset_student_attempts_url'] }"> </p>
%endif
<p>
<!-- Doesn't give any type of notification upon success -->
<input type="button" name="reset-attempts-single" value="${_("Reset Student Attempts")}" data-endpoint="${ section_data['reset_student_attempts_url'] }">
%if settings.MITX_FEATURES.get('ENABLE_INSTRUCTOR_BACKGROUND_TASKS') and section_data['access']['instructor']: %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'] }"> <input type="button" name="rescore-problem-single" value="${_("Rescore Student Submission")}" data-endpoint="${ section_data['rescore_problem_url'] }">
%endif %endif
</p>
<p>
%if section_data['access']['instructor']:
<p> ${_('You may also delete the entire state of a student for the specified problem:')} </p>
<input type="button" class="molly-guard" name="delete-state-single" value="${_("Delete Student State for Problem")}" data-endpoint="${ section_data['reset_student_attempts_url'] }">
%endif
</p>
%if settings.MITX_FEATURES.get('ENABLE_INSTRUCTOR_BACKGROUND_TASKS') and section_data['access']['instructor']: %if settings.MITX_FEATURES.get('ENABLE_INSTRUCTOR_BACKGROUND_TASKS') and section_data['access']['instructor']:
<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 on the Course Info tab. "
"To see status for all tasks submitted for this problem and student, click on this button:")} "To see status for all tasks submitted for this problem and student, click on this button:")}
</p> </p>
...@@ -76,18 +111,11 @@ ...@@ -76,18 +111,11 @@
</p> </p>
<p> <p>
<p> <p>
${_("These actions run in the background, and status for active tasks will appear in a table below. " ${_("These actions run in the background, and status for active tasks will appear in a table on the Course Info tab. "
"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>
<div class="running-tasks-container action-type-container">
<hr>
<h2> ${_("Pending Instructor Tasks")} </h2>
<div class="running-tasks-table" data-endpoint="${ section_data['list_instructor_tasks_url'] }"></div>
</div>
%endif %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