Commit 07e76b3b by Sarina Canelake

Enable use of student usernames on student admin page

This is in addition to email addresses, which also work.
parent 8a2bd25b
...@@ -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,14 @@ def reset_student_attempts(request, course_id): ...@@ -510,14 +509,14 @@ 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 = get_student_from_identifier(request.GET.get('unique_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,9 +533,8 @@ def reset_student_attempts(request, course_id): ...@@ -534,9 +533,8 @@ 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.")
...@@ -561,21 +559,24 @@ def rescore_problem(request, course_id): ...@@ -561,21 +559,24 @@ 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 = 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] 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 +584,8 @@ def rescore_problem(request, course_id): ...@@ -583,9 +584,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
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 +608,22 @@ def list_instructor_tasks(request, course_id): ...@@ -608,21 +608,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)
......
""" """
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
...@@ -118,15 +118,15 @@ class StudentAdmin ...@@ -118,15 +118,15 @@ class StudentAdmin
# go to student progress page # go to student progress page
@$progress_link.click (e) => @$progress_link.click (e) =>
e.preventDefault() e.preventDefault()
email = @$field_student_select_progress.val() unique_student_identifier = @$field_student_select_progress.val()
$.ajax $.ajax
dataType: 'json' dataType: 'json'
url: @$progress_link.data 'endpoint' url: @$progress_link.data 'endpoint'
data: student_email: email data: unique_student_identifier: unique_student_identifier
success: @clear_errors_then (data) -> success: @clear_errors_then (data) ->
window.location = data.progress_url 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 # enroll student
@$btn_enroll.click => @$btn_enroll.click =>
...@@ -158,7 +158,7 @@ class StudentAdmin ...@@ -158,7 +158,7 @@ class StudentAdmin
# reset attempts for student on problem # reset attempts for student on problem
@$btn_reset_attempts_single.click => @$btn_reset_attempts_single.click =>
send_data = 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() problem_to_reset: @$field_problem_select_single.val()
delete_module: false delete_module: false
...@@ -167,14 +167,14 @@ class StudentAdmin ...@@ -167,14 +167,14 @@ class StudentAdmin
url: @$btn_reset_attempts_single.data 'endpoint' url: @$btn_reset_attempts_single.data 'endpoint'
data: send_data data: send_data
success: @clear_errors_then -> console.log 'problem attempts reset' 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 # delete state for student on problem
@$btn_delete_state_single.click => confirm_then @$btn_delete_state_single.click => confirm_then
msg: "Delete student '#{@$field_student_select_grade.val()}'s state on problem '#{@$field_problem_select_single.val()}'?" msg: "Delete student '#{@$field_student_select_grade.val()}'s state on problem '#{@$field_problem_select_single.val()}'?"
ok: => ok: =>
send_data = 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() problem_to_reset: @$field_problem_select_single.val()
delete_module: true delete_module: true
...@@ -188,7 +188,7 @@ class StudentAdmin ...@@ -188,7 +188,7 @@ class StudentAdmin
# start task to rescore problem for student # start task to rescore problem for student
@$btn_rescore_problem_single.click => @$btn_rescore_problem_single.click =>
send_data = 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() problem_to_reset: @$field_problem_select_single.val()
$.ajax $.ajax
...@@ -201,13 +201,13 @@ class StudentAdmin ...@@ -201,13 +201,13 @@ class StudentAdmin
# list task history for student+problem # list task history for student+problem
@$btn_task_history_single.click => @$btn_task_history_single.click =>
send_data = send_data =
student_email: @$field_student_select_grade.val() unique_student_identifier: @$field_student_select_grade.val()
problem_urlname: @$field_problem_select_single.val() problem_urlname: @$field_problem_select_single.val()
if not send_data.student_email if not send_data.unique_student_identifier
return @$request_response_error_single.text "Enter a student email." return @$request_response_error_single.text "Please enter a student email address or username."
if not send_data.problem_urlname 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 $.ajax
dataType: 'json' dataType: 'json'
......
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