${_('Specify a particular problem in the course here by its url:')}
+
+
+
${_("Student-specific grade adjustment")}
+
+
+
+ ${_("Specify the {platform_name} email address or username of a student here:").format(platform_name=settings.PLATFORM_NAME)}
+
+
+
+
+
${_('Specify a particular problem in the course here by its url:')}
+
+
${_('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="i4x://university/course/problem/problemname",
@@ -29,20 +51,31 @@
urlname2="notaproblem/someothername")
}
-
- %if section_data['access']['instructor']:
-
${_('You may also delete the entire state of a student for the specified module:')}
-
- %endif
+
+ ${_("Next, select an action to perform for the given user and problem:")}
+
+
+
+
+
%if settings.MITX_FEATURES.get('ENABLE_INSTRUCTOR_BACKGROUND_TASKS') and section_data['access']['instructor']:
%endif
+
+
+
+ %if section_data['access']['instructor']:
+
${_('You may also delete the entire state of a student for the specified problem:')}
+
+ %endif
+
+
%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. "
+ ${_("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:")}
@@ -76,18 +109,11 @@
- ${_("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")}:
-
-
-
-
-
${_("Pending Instructor Tasks")}
-
-
%endif
From 07e76b3b2f92d7f97e2ad1f3a3f094885426b55e Mon Sep 17 00:00:00 2001
From: Sarina Canelake
Date: Thu, 26 Sep 2013 08:09:09 -0400
Subject: [PATCH 03/13] Enable use of student usernames on student admin page
This is in addition to email addresses, which also work.
---
lms/djangoapps/instructor/views/api.py | 53 ++++++++++---------
lms/djangoapps/instructor/views/tools.py | 17 ++++++
.../instructor_dashboard/student_admin.coffee | 22 ++++----
3 files changed, 55 insertions(+), 37 deletions(-)
diff --git a/lms/djangoapps/instructor/views/api.py b/lms/djangoapps/instructor/views/api.py
index 8a552feb66..9e58ecea5f 100644
--- a/lms/djangoapps/instructor/views/api.py
+++ b/lms/djangoapps/instructor/views/api.py
@@ -33,7 +33,7 @@ import instructor_task.api
from instructor_task.api_helper import AlreadyRunningError
import instructor.enrollment as enrollment
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 analytics.basic
import analytics.distributions
@@ -456,20 +456,19 @@ def get_distribution(request, course_id):
@common_exceptions_400
@require_level('staff')
@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):
"""
Get the progress url of a student.
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. {
'progress_url': '/../...'
}
"""
- student_email = strip_if_string(request.GET.get('student_email'))
- user = User.objects.get(email=student_email)
+ user = get_student_from_identifier(request.GET.get('unique_student_identifier'))
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):
Takes some of the following query paremeters
- 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
requires instructor access
mutually exclusive with delete_module
@@ -510,14 +509,14 @@ def reset_student_attempts(request, course_id):
)
problem_to_reset = strip_if_string(request.GET.get('problem_to_reset'))
- student_email = strip_if_string(request.GET.get('student_email'))
+ student = get_student_from_identifier(request.GET.get('unique_student_identifier'))
all_students = request.GET.get('all_students', False) in ['true', 'True', True]
delete_module = request.GET.get('delete_module', False) in ['true', 'True', True]
# parameter combinations
- if all_students and student_email:
+ if all_students and student:
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:
return HttpResponseBadRequest(
@@ -534,9 +533,8 @@ def reset_student_attempts(request, course_id):
response_payload = {}
response_payload['problem_to_reset'] = problem_to_reset
- if student_email:
+ if student:
try:
- student = User.objects.get(email=student_email)
enrollment.reset_student_attempts(course_id, student, module_state_key, delete_module=delete_module)
except StudentModule.DoesNotExist:
return HttpResponseBadRequest("Module does not exist.")
@@ -561,21 +559,24 @@ def rescore_problem(request, course_id):
Takes either of the following query paremeters
- 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 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'))
- 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)
+
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.")
- if all_students and student_email:
+ if all_students and student:
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)
@@ -583,9 +584,8 @@ def rescore_problem(request, course_id):
response_payload = {}
response_payload['problem_to_reset'] = problem_to_reset
- if student_email:
- response_payload['student_email'] = student_email
- student = User.objects.get(email=student_email)
+ if student:
+ response_payload['student'] = student
instructor_task.api.submit_rescore_problem_for_student(request, course_id, module_state_key, student)
response_payload['task'] = 'created'
elif all_students:
@@ -608,21 +608,22 @@ def list_instructor_tasks(request, course_id):
Takes optional query paremeters.
- With no arguments, lists running tasks.
- `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)
"""
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(
- "student_email must accompany problem_urlname"
+ "unique_student_identifier must accompany problem_urlname"
)
if problem_urlname:
module_state_key = _msk_from_problem_urlname(course_id, problem_urlname)
- if student_email:
- student = User.objects.get(email=student_email)
+ if student:
tasks = instructor_task.api.get_instructor_task_history(course_id, module_state_key, student)
else:
tasks = instructor_task.api.get_instructor_task_history(course_id, module_state_key)
diff --git a/lms/djangoapps/instructor/views/tools.py b/lms/djangoapps/instructor/views/tools.py
index 11fc135976..eab0e66196 100644
--- a/lms/djangoapps/instructor/views/tools.py
+++ b/lms/djangoapps/instructor/views/tools.py
@@ -1,7 +1,24 @@
"""
Tools for the instructor dashboard
"""
+from django.contrib.auth.models import User
+
def strip_if_string(value):
if isinstance(value, basestring):
return value.strip()
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
diff --git a/lms/static/coffee/src/instructor_dashboard/student_admin.coffee b/lms/static/coffee/src/instructor_dashboard/student_admin.coffee
index e607a463fc..10d83c4a00 100644
--- a/lms/static/coffee/src/instructor_dashboard/student_admin.coffee
+++ b/lms/static/coffee/src/instructor_dashboard/student_admin.coffee
@@ -118,15 +118,15 @@ class StudentAdmin
# go to student progress page
@$progress_link.click (e) =>
e.preventDefault()
- email = @$field_student_select_progress.val()
+ unique_student_identifier = @$field_student_select_progress.val()
$.ajax
dataType: 'json'
url: @$progress_link.data 'endpoint'
- data: student_email: email
+ data: unique_student_identifier: unique_student_identifier
success: @clear_errors_then (data) ->
window.location = data.progress_url
- error: std_ajax_err => @$request_response_error_single.text "Error getting student progress url for '#{email}'."
+ error: std_ajax_err => @$request_response_error_single.text "Error getting student progress url for '#{unique_student_identifier}'."
# enroll student
@$btn_enroll.click =>
@@ -158,7 +158,7 @@ class StudentAdmin
# reset attempts for student on problem
@$btn_reset_attempts_single.click =>
send_data =
- student_email: @$field_student_select_grade.val()
+ unique_student_identifier: @$field_student_select_grade.val()
problem_to_reset: @$field_problem_select_single.val()
delete_module: false
@@ -167,14 +167,14 @@ class StudentAdmin
url: @$btn_reset_attempts_single.data 'endpoint'
data: send_data
success: @clear_errors_then -> console.log 'problem attempts reset'
- error: std_ajax_err => @$request_response_error_single.text "Error resetting problem attempts."
+ error: std_ajax_err => @$request_response_error_single.text "Error resetting problem attempts for problem '#{problem_to_reset}' and student '#{unique_student_identifier}'."
# delete state for student on problem
@$btn_delete_state_single.click => confirm_then
msg: "Delete student '#{@$field_student_select_grade.val()}'s state on problem '#{@$field_problem_select_single.val()}'?"
ok: =>
send_data =
- student_email: @$field_student_select_grade.val()
+ unique_student_identifier: @$field_student_select_grade.val()
problem_to_reset: @$field_problem_select_single.val()
delete_module: true
@@ -188,7 +188,7 @@ class StudentAdmin
# start task to rescore problem for student
@$btn_rescore_problem_single.click =>
send_data =
- student_email: @$field_student_select_grade.val()
+ unique_student_identifier: @$field_student_select_grade.val()
problem_to_reset: @$field_problem_select_single.val()
$.ajax
@@ -201,13 +201,13 @@ class StudentAdmin
# list task history for student+problem
@$btn_task_history_single.click =>
send_data =
- student_email: @$field_student_select_grade.val()
+ unique_student_identifier: @$field_student_select_grade.val()
problem_urlname: @$field_problem_select_single.val()
- if not send_data.student_email
- return @$request_response_error_single.text "Enter a student email."
+ if not send_data.unique_student_identifier
+ return @$request_response_error_single.text "Please enter a student email address or username."
if not send_data.problem_urlname
- return @$request_response_error_single.text "Enter a problem urlname."
+ return @$request_response_error_single.text "Please enter a problem urlname."
$.ajax
dataType: 'json'
From 086f544a1a39d3cac22c30bd8c0ee3d693fd0748 Mon Sep 17 00:00:00 2001
From: Sarina Canelake
Date: Thu, 26 Sep 2013 17:57:55 -0400
Subject: [PATCH 04/13] Fix, and add more, tests
---
lms/djangoapps/instructor/tests/test_api.py | 41 +++++++++++++++++----
lms/djangoapps/instructor/views/api.py | 16 +++++---
2 files changed, 44 insertions(+), 13 deletions(-)
diff --git a/lms/djangoapps/instructor/tests/test_api.py b/lms/djangoapps/instructor/tests/test_api.py
index c5b1b21b52..a32217ab30 100644
--- a/lms/djangoapps/instructor/tests/test_api.py
+++ b/lms/djangoapps/instructor/tests/test_api.py
@@ -512,7 +512,7 @@ class TestInstructorAPILevelsDataDump(ModuleStoreTestCase, LoginEnrollmentTestCa
def test_get_student_progress_url(self):
""" Test that progress_url is in the successful response. """
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"))
)
print url
@@ -522,6 +522,19 @@ class TestInstructorAPILevelsDataDump(ModuleStoreTestCase, LoginEnrollmentTestCa
res_json = json.loads(response.content)
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):
""" Test that the endpoint 404's without the required query params. """
url = reverse('get_student_progress_url', kwargs={'course_id': self.course.id})
@@ -579,7 +592,7 @@ class TestInstructorAPIRegradeTask(ModuleStoreTestCase, LoginEnrollmentTestCase)
url = reverse('reset_student_attempts', kwargs={'course_id': self.course.id})
response = self.client.get(url, {
'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)
@@ -608,7 +621,7 @@ class TestInstructorAPIRegradeTask(ModuleStoreTestCase, LoginEnrollmentTestCase)
url = reverse('reset_student_attempts', kwargs={'course_id': self.course.id})
response = self.client.get(url, {
'problem_to_reset': 'robot-not-a-real-module',
- 'student_email': self.student.email,
+ 'unique_student_identifier': self.student.email,
})
print response.content
self.assertEqual(response.status_code, 400)
@@ -618,7 +631,7 @@ class TestInstructorAPIRegradeTask(ModuleStoreTestCase, LoginEnrollmentTestCase)
url = reverse('reset_student_attempts', kwargs={'course_id': self.course.id})
response = self.client.get(url, {
'problem_to_reset': self.problem_urlname,
- 'student_email': self.student.email,
+ 'unique_student_identifier': self.student.email,
'delete_module': True,
})
print response.content
@@ -634,11 +647,11 @@ class TestInstructorAPIRegradeTask(ModuleStoreTestCase, LoginEnrollmentTestCase)
)
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})
response = self.client.get(url, {
'problem_to_reset': self.problem_urlname,
- 'student_email': self.student.email,
+ 'unique_student_identifier': self.student.email,
'all_students': True,
})
print response.content
@@ -650,7 +663,19 @@ class TestInstructorAPIRegradeTask(ModuleStoreTestCase, LoginEnrollmentTestCase)
url = reverse('rescore_problem', kwargs={'course_id': self.course.id})
response = self.client.get(url, {
'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
self.assertEqual(response.status_code, 200)
@@ -747,7 +772,7 @@ class TestInstructorAPITaskLists(ModuleStoreTestCase, LoginEnrollmentTestCase):
url = reverse('list_instructor_tasks', kwargs={'course_id': self.course.id})
response = self.client.get(url, {
'problem_urlname': self.problem_urlname,
- 'student_email': self.student.email,
+ 'unique_student_identifier': self.student.email,
})
print response.content
self.assertEqual(response.status_code, 200)
diff --git a/lms/djangoapps/instructor/views/api.py b/lms/djangoapps/instructor/views/api.py
index 9e58ecea5f..e0b047604e 100644
--- a/lms/djangoapps/instructor/views/api.py
+++ b/lms/djangoapps/instructor/views/api.py
@@ -509,7 +509,10 @@ def reset_student_attempts(request, course_id):
)
problem_to_reset = strip_if_string(request.GET.get('problem_to_reset'))
- student = get_student_from_identifier(request.GET.get('unique_student_identifier'))
+ 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]
delete_module = request.GET.get('delete_module', False) in ['true', 'True', True]
@@ -538,9 +541,11 @@ def reset_student_attempts(request, course_id):
enrollment.reset_student_attempts(course_id, student, module_state_key, delete_module=delete_module)
except StudentModule.DoesNotExist:
return HttpResponseBadRequest("Module does not exist.")
+ response_payload['student'] = student_identifier
elif all_students:
instructor_task.api.submit_reset_problem_attempts_for_all_students(request, course_id, module_state_key)
response_payload['task'] = 'created'
+ response_payload['student'] = 'All Students'
else:
return HttpResponseBadRequest()
@@ -565,9 +570,10 @@ def rescore_problem(request, course_id):
all_students and unique_student_identifier cannot both be present.
"""
problem_to_reset = strip_if_string(request.GET.get('problem_to_reset'))
- student = request.GET.get('unique_student_identifier', None)
- if student is not None:
- student = get_student_from_identifier(student)
+ 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]
@@ -585,7 +591,7 @@ def rescore_problem(request, course_id):
response_payload['problem_to_reset'] = problem_to_reset
if student:
- response_payload['student'] = student
+ response_payload['student'] = student_identifier
instructor_task.api.submit_rescore_problem_for_student(request, course_id, module_state_key, student)
response_payload['task'] = 'created'
elif all_students:
From 91d85f5c2a1b1a43f282c64a7f78a84bd2aecc09 Mon Sep 17 00:00:00 2001
From: Sarina Canelake
Date: Thu, 26 Sep 2013 18:54:15 -0400
Subject: [PATCH 05/13] Enable alerts when a task succeeds.
Note: Alerts are pop-up boxes. Not sure if this is desireable, but I couldn't figure out
how to make a success message show up in the same place that error messages do.
---
lms/djangoapps/instructor/views/tools.py | 1 +
.../instructor_dashboard/student_admin.coffee | 30 +++++++++++--------
2 files changed, 19 insertions(+), 12 deletions(-)
diff --git a/lms/djangoapps/instructor/views/tools.py b/lms/djangoapps/instructor/views/tools.py
index eab0e66196..cbf6b6468a 100644
--- a/lms/djangoapps/instructor/views/tools.py
+++ b/lms/djangoapps/instructor/views/tools.py
@@ -8,6 +8,7 @@ def strip_if_string(value):
return value.strip()
return value
+
def get_student_from_identifier(unique_student_identifier):
"""
Gets a student object using either an email address or username.
diff --git a/lms/static/coffee/src/instructor_dashboard/student_admin.coffee b/lms/static/coffee/src/instructor_dashboard/student_admin.coffee
index 10d83c4a00..3030f622ce 100644
--- a/lms/static/coffee/src/instructor_dashboard/student_admin.coffee
+++ b/lms/static/coffee/src/instructor_dashboard/student_admin.coffee
@@ -157,16 +157,18 @@ class StudentAdmin
# reset attempts for student on problem
@$btn_reset_attempts_single.click =>
+ unique_student_identifier = @$field_student_select_grade.val()
+ problem_to_reset = @$field_problem_select_single.val()
send_data =
- unique_student_identifier: @$field_student_select_grade.val()
- problem_to_reset: @$field_problem_select_single.val()
+ unique_student_identifier: unique_student_identifier
+ problem_to_reset: problem_to_reset
delete_module: false
$.ajax
dataType: 'json'
url: @$btn_reset_attempts_single.data 'endpoint'
data: send_data
- success: @clear_errors_then -> console.log 'problem attempts reset'
+ success: @clear_errors_then -> alert "Success! Problem attempts reset for problem '#{problem_to_reset}' and student '#{unique_student_identifier}'."
error: std_ajax_err => @$request_response_error_single.text "Error resetting problem attempts for problem '#{problem_to_reset}' and student '#{unique_student_identifier}'."
# delete state for student on problem
@@ -182,21 +184,23 @@ class StudentAdmin
dataType: 'json'
url: @$btn_delete_state_single.data 'endpoint'
data: send_data
- success: @clear_errors_then -> console.log 'module state deleted'
+ success: @clear_errors_then -> alert 'Module state successfully deleted.'
error: std_ajax_err => @$request_response_error_single.text "Error deleting problem state."
# start task to rescore problem for student
@$btn_rescore_problem_single.click =>
+ unique_student_identifier = @$field_student_select_grade.val()
+ problem_to_reset = @$field_problem_select_single.val()
send_data =
- unique_student_identifier: @$field_student_select_grade.val()
- problem_to_reset: @$field_problem_select_single.val()
+ unique_student_identifier: unique_student_identifier
+ problem_to_reset: problem_to_reset
$.ajax
dataType: 'json'
url: @$btn_rescore_problem_single.data 'endpoint'
data: send_data
- success: @clear_errors_then -> console.log 'started rescore problem task'
- error: std_ajax_err => @$request_response_error_single.text "Error starting a task to rescore student's problem."
+ success: @clear_errors_then -> alert "Started rescore problem task for problem '#{problem_to_reset}' and student '#{unique_student_identifier}'. Click the 'Show Background Task History for Student' button to see the status of the task."
+ error: std_ajax_err => @$request_response_error_single.text "Error starting a task to rescore problem '#{problem_to_reset}' for student '#{unique_student_identifier}'."
# list task history for student+problem
@$btn_task_history_single.click =>
@@ -221,30 +225,32 @@ class StudentAdmin
@$btn_reset_attempts_all.click => confirm_then
msg: "Reset attempts for all students on problem '#{@$field_problem_select_all.val()}'?"
ok: =>
+ problem_to_reset = @$field_problem_select_all.val()
send_data =
all_students: true
- problem_to_reset: @$field_problem_select_all.val()
+ problem_to_reset: problem_to_reset
$.ajax
dataType: 'json'
url: @$btn_reset_attempts_all.data 'endpoint'
data: send_data
- success: @clear_errors_then -> console.log 'started reset attempts task'
+ success: @clear_errors_then -> alert "Successfully started task to reset attempts for problem '#{problem_to_reset}'. Click the 'Show Background Task History for Problem' button to see the status of the task."
error: std_ajax_err => @$request_response_error_all.text "Error starting a task to reset attempts for all students on this problem."
# start task to rescore problem for all students
@$btn_rescore_problem_all.click => confirm_then
msg: "Rescore problem '#{@$field_problem_select_all.val()}' for all students?"
ok: =>
+ problem_to_reset = @$field_problem_select_all.val()
send_data =
all_students: true
- problem_to_reset: @$field_problem_select_all.val()
+ problem_to_reset: problem_to_reset
$.ajax
dataType: 'json'
url: @$btn_rescore_problem_all.data 'endpoint'
data: send_data
- success: @clear_errors_then -> console.log 'started rescore problem task'
+ success: @clear_errors_then -> alert "Successfully started task to rescore problem '#{problem_to_reset}' for all students. Click the 'Show Background Task History for Problem' button to see the status of the task."
error: std_ajax_err => @$request_response_error_all.text "Error starting a task to rescore this problem for all students."
# list task history for problem
From 3813e51cfbb684c1a7b5efd93378eeec5a5a71e8 Mon Sep 17 00:00:00 2001
From: Sarina Canelake
Date: Fri, 27 Sep 2013 09:42:51 -0400
Subject: [PATCH 06/13] Add more feedback for instructors on Student Admin
section of the dash.
---
.../instructor_dashboard/student_admin.coffee | 93 +++++++++++++------
.../instructor_dashboard_2/student_admin.html | 4 +-
2 files changed, 66 insertions(+), 31 deletions(-)
diff --git a/lms/static/coffee/src/instructor_dashboard/student_admin.coffee b/lms/static/coffee/src/instructor_dashboard/student_admin.coffee
index 3030f622ce..3e5c8c27c2 100644
--- a/lms/static/coffee/src/instructor_dashboard/student_admin.coffee
+++ b/lms/static/coffee/src/instructor_dashboard/student_admin.coffee
@@ -101,7 +101,8 @@ class StudentAdmin
@$table_running_tasks = @$section.find ".running-tasks-table"
# response areas
- @$request_response_error_single = find_and_assert @$section, ".student-specific-container .request-response-error"
+ @$request_response_error_progress = find_and_assert @$section, ".student-specific-container .request-response-error"
+ @$request_response_error_grade = find_and_assert @$section, ".student-grade-container .request-response-error"
@$request_response_error_all = @$section.find ".course-specific-container .request-response-error"
# start polling for task list
@@ -119,6 +120,8 @@ class StudentAdmin
@$progress_link.click (e) =>
e.preventDefault()
unique_student_identifier = @$field_student_select_progress.val()
+ if not unique_student_identifier
+ return @$request_response_error_progress.text "Please enter a student email address or username."
$.ajax
dataType: 'json'
@@ -126,7 +129,7 @@ class StudentAdmin
data: unique_student_identifier: unique_student_identifier
success: @clear_errors_then (data) ->
window.location = data.progress_url
- error: std_ajax_err => @$request_response_error_single.text "Error getting student progress url for '#{unique_student_identifier}'."
+ error: std_ajax_err => @$request_response_error_progress.text "Error getting student progress url for '#{unique_student_identifier}'."
# enroll student
@$btn_enroll.click =>
@@ -140,7 +143,7 @@ class StudentAdmin
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}'."
+ error: std_ajax_err => @$request_response_error_progress.text "Error enrolling student '#{send_data.emails}'."
# unenroll student
@$btn_unenroll.click =>
@@ -153,12 +156,16 @@ class StudentAdmin
url: @$btn_unenroll.data 'endpoint'
data: send_data
success: @clear_errors_then -> console.log "student #{send_data.emails} unenrolled"
- error: std_ajax_err => @$request_response_error_single.text "Error unenrolling student '#{send_data.emails}'."
+ error: std_ajax_err => @$request_response_error_progress.text "Error unenrolling student '#{send_data.emails}'."
# reset attempts for student on problem
@$btn_reset_attempts_single.click =>
unique_student_identifier = @$field_student_select_grade.val()
problem_to_reset = @$field_problem_select_single.val()
+ if not unique_student_identifier
+ return @$request_response_error_grade.text "Please enter a student email address or username."
+ if not problem_to_reset
+ return @$request_response_error_grade.text "Please enter a problem urlname."
send_data =
unique_student_identifier: unique_student_identifier
problem_to_reset: problem_to_reset
@@ -169,15 +176,21 @@ class StudentAdmin
url: @$btn_reset_attempts_single.data 'endpoint'
data: send_data
success: @clear_errors_then -> alert "Success! Problem attempts reset for problem '#{problem_to_reset}' and student '#{unique_student_identifier}'."
- error: std_ajax_err => @$request_response_error_single.text "Error resetting problem attempts for problem '#{problem_to_reset}' and student '#{unique_student_identifier}'."
+ error: std_ajax_err => @$request_response_error_grade.text "Error resetting problem attempts for problem '#{problem_to_reset}' and student '#{unique_student_identifier}'."
# delete state for student on problem
- @$btn_delete_state_single.click => confirm_then
- msg: "Delete student '#{@$field_student_select_grade.val()}'s state on problem '#{@$field_problem_select_single.val()}'?"
- ok: =>
+ @$btn_delete_state_single.click =>
+ unique_student_identifier = @$field_student_select_grade.val()
+ problem_to_reset = @$field_problem_select_single.val()
+ if not unique_student_identifier
+ return @$request_response_error_grade.text "Please enter a student email address or username."
+ if not problem_to_reset
+ return @$request_response_error_grade.text "Please enter a problem urlname."
+
+ if window.confirm "Delete student '#{unique_student_identifier}'s state on problem '#{problem_to_reset}'?"
send_data =
- unique_student_identifier: @$field_student_select_grade.val()
- problem_to_reset: @$field_problem_select_single.val()
+ unique_student_identifier: unique_student_identifier
+ problem_to_reset: problem_to_reset
delete_module: true
$.ajax
@@ -185,12 +198,18 @@ class StudentAdmin
url: @$btn_delete_state_single.data 'endpoint'
data: send_data
success: @clear_errors_then -> alert 'Module state successfully deleted.'
- error: std_ajax_err => @$request_response_error_single.text "Error deleting problem state."
+ error: std_ajax_err => @$request_response_error_grade.text "Error deleting problem state."
+ else
+ @clear_errors()
# start task to rescore problem for student
@$btn_rescore_problem_single.click =>
unique_student_identifier = @$field_student_select_grade.val()
problem_to_reset = @$field_problem_select_single.val()
+ if not unique_student_identifier
+ return @$request_response_error_grade.text "Please enter a student email address or username."
+ if not problem_to_reset
+ return @$request_response_error_grade.text "Please enter a problem urlname."
send_data =
unique_student_identifier: unique_student_identifier
problem_to_reset: problem_to_reset
@@ -200,18 +219,19 @@ class StudentAdmin
url: @$btn_rescore_problem_single.data 'endpoint'
data: send_data
success: @clear_errors_then -> alert "Started rescore problem task for problem '#{problem_to_reset}' and student '#{unique_student_identifier}'. Click the 'Show Background Task History for Student' button to see the status of the task."
- error: std_ajax_err => @$request_response_error_single.text "Error starting a task to rescore problem '#{problem_to_reset}' for student '#{unique_student_identifier}'."
+ error: std_ajax_err => @$request_response_error_grade.text "Error starting a task to rescore problem '#{problem_to_reset}' for student '#{unique_student_identifier}'."
# list task history for student+problem
@$btn_task_history_single.click =>
+ unique_student_identifier = @$field_student_select_grade.val()
+ problem_to_reset = @$field_problem_select_single.val()
+ if not unique_student_identifier
+ return @$request_response_error_grade.text "Please enter a student email address or username."
+ if not problem_to_reset
+ return @$request_response_error_grade.text "Please enter a problem urlname."
send_data =
- unique_student_identifier: @$field_student_select_grade.val()
- problem_urlname: @$field_problem_select_single.val()
-
- if not send_data.unique_student_identifier
- return @$request_response_error_single.text "Please enter a student email address or username."
- if not send_data.problem_urlname
- return @$request_response_error_single.text "Please enter a problem urlname."
+ unique_student_identifier: unique_student_identifier
+ problem_urlname: problem_to_reset
$.ajax
dataType: 'json'
@@ -219,13 +239,14 @@ class StudentAdmin
data: send_data
success: @clear_errors_then (data) =>
create_task_list_table @$table_task_history_single, data.tasks
- error: std_ajax_err => @$request_response_error_single.text "Error getting task history for student+problem"
+ error: std_ajax_err => @$request_response_error_grade.text "Error getting task history for student '#{unique_student_identifier}' and problem '#{problem_to_reset}'."
# start task to reset attempts on problem for all students
- @$btn_reset_attempts_all.click => confirm_then
- msg: "Reset attempts for all students on problem '#{@$field_problem_select_all.val()}'?"
- ok: =>
- problem_to_reset = @$field_problem_select_all.val()
+ @$btn_reset_attempts_all.click =>
+ problem_to_reset = @$field_problem_select_all.val()
+ if not problem_to_reset
+ return @$request_response_error_all.text "Please enter a problem urlname."
+ if window.confirm "Reset attempts for all students on problem '#{@$field_problem_select_all.val()}'?"
send_data =
all_students: true
problem_to_reset: problem_to_reset
@@ -236,12 +257,15 @@ class StudentAdmin
data: send_data
success: @clear_errors_then -> alert "Successfully started task to reset attempts for problem '#{problem_to_reset}'. Click the 'Show Background Task History for Problem' button to see the status of the task."
error: std_ajax_err => @$request_response_error_all.text "Error starting a task to reset attempts for all students on this problem."
+ else
+ @clear_errors()
# start task to rescore problem for all students
- @$btn_rescore_problem_all.click => confirm_then
- msg: "Rescore problem '#{@$field_problem_select_all.val()}' for all students?"
- ok: =>
- problem_to_reset = @$field_problem_select_all.val()
+ @$btn_rescore_problem_all.click =>
+ problem_to_reset = @$field_problem_select_all.val()
+ if not problem_to_reset
+ return @$request_response_error_all.text "Please enter a problem urlname."
+ if window.confirm "Rescore problem '#{@$field_problem_select_all.val()}' for all students?"
send_data =
all_students: true
problem_to_reset: problem_to_reset
@@ -252,6 +276,8 @@ class StudentAdmin
data: send_data
success: @clear_errors_then -> alert "Successfully started task to rescore problem '#{problem_to_reset}' for all students. Click the 'Show Background Task History for Problem' button to see the status of the task."
error: std_ajax_err => @$request_response_error_all.text "Error starting a task to rescore this problem for all students."
+ else
+ @clear_errors()
# list task history for problem
@$btn_task_history_all.click =>
@@ -259,7 +285,7 @@ class StudentAdmin
problem_urlname: @$field_problem_select_all.val()
if not send_data.problem_urlname
- return @$request_response_error_all.text "Enter a problem urlname."
+ return @$request_response_error_all.text "Please enter a problem urlname."
$.ajax
dataType: 'json'
@@ -279,11 +305,18 @@ class StudentAdmin
# wraps a function, but first clear the error displays
clear_errors_then: (cb) ->
- @$request_response_error_single.empty()
+ @$request_response_error_progress.empty()
+ @$request_response_error_grade.empty()
@$request_response_error_all.empty()
->
cb?.apply this, arguments
+
+ clear_errors: ->
+ @$request_response_error_progress.empty()
+ @$request_response_error_grade.empty()
+ @$request_response_error_all.empty()
+
# handler for when the section title is clicked.
onClickTitle: -> @task_poller?.start()
diff --git a/lms/templates/instructor/instructor_dashboard_2/student_admin.html b/lms/templates/instructor/instructor_dashboard_2/student_admin.html
index 001987ec18..f3fc7b6054 100644
--- a/lms/templates/instructor/instructor_dashboard_2/student_admin.html
+++ b/lms/templates/instructor/instructor_dashboard_2/student_admin.html
@@ -29,9 +29,11 @@
-->
+
+
${_("Student-specific grade adjustment")}
-
+
${_("Specify the {platform_name} email address or username of a student here:").format(platform_name=settings.PLATFORM_NAME)}
From f4971e753b729bca6eb1e86fbdae374a65b3b6e9 Mon Sep 17 00:00:00 2001
From: Greg Price
Date: Fri, 27 Sep 2013 13:45:38 -0400
Subject: [PATCH 07/13] Add accessible labels for forum post body inputs
---
lms/static/coffee/src/customwmd.coffee | 4 +++-
1 file changed, 3 insertions(+), 1 deletion(-)
diff --git a/lms/static/coffee/src/customwmd.coffee b/lms/static/coffee/src/customwmd.coffee
index 838112059e..2dabc3ed15 100644
--- a/lms/static/coffee/src/customwmd.coffee
+++ b/lms/static/coffee/src/customwmd.coffee
@@ -131,9 +131,11 @@ $ ->
initialText = $elem.html()
$elem.empty()
_append = appended_id || ""
+ wmdInputId = "wmd-input#{_append}"
$wmdPanel = $("