Add enrollment status check for instructor
To the Student Admin tab of the instructor dashboard, add the ability to check a specific learner's enrollment status.
This commit is contained in:
committed by
Jillian Vogel
parent
7517eb918b
commit
efc3057fe1
@@ -180,6 +180,7 @@ INSTRUCTOR_POST_ENDPOINTS = set([
|
||||
'get_problem_responses',
|
||||
'get_proctored_exam_results',
|
||||
'get_registration_codes',
|
||||
'get_student_enrollment_status',
|
||||
'get_student_progress_url',
|
||||
'get_students_features',
|
||||
'get_students_who_may_enroll',
|
||||
@@ -1911,6 +1912,65 @@ class TestInstructorAPIEnrollment(SharedModuleStoreTestCase, LoginEnrollmentTest
|
||||
self.assertEqual(response.status_code, 200)
|
||||
return response
|
||||
|
||||
def test_get_enrollment_status(self):
|
||||
"""Check that enrollment states are reported correctly."""
|
||||
|
||||
# enrolled, active
|
||||
url = reverse(
|
||||
'get_student_enrollment_status',
|
||||
kwargs={'course_id': self.course.id.to_deprecated_string()},
|
||||
)
|
||||
params = {
|
||||
'unique_student_identifier': 'EnrolledStudent'
|
||||
}
|
||||
response = self.client.post(url, params)
|
||||
self.assertEqual(response.status_code, 200)
|
||||
res_json = json.loads(response.content)
|
||||
self.assertEqual(
|
||||
res_json['enrollment_status'],
|
||||
'Enrollment status for EnrolledStudent: active'
|
||||
)
|
||||
|
||||
# unenrolled, inactive
|
||||
CourseEnrollment.unenroll(
|
||||
self.enrolled_student,
|
||||
self.course.id
|
||||
)
|
||||
|
||||
response = self.client.post(url, params)
|
||||
self.assertEqual(response.status_code, 200)
|
||||
res_json = json.loads(response.content)
|
||||
self.assertEqual(
|
||||
res_json['enrollment_status'],
|
||||
'Enrollment status for EnrolledStudent: inactive'
|
||||
)
|
||||
|
||||
# invited, not yet registered
|
||||
params = {
|
||||
'unique_student_identifier': 'robot-allowed@robot.org'
|
||||
}
|
||||
|
||||
response = self.client.post(url, params)
|
||||
self.assertEqual(response.status_code, 200)
|
||||
res_json = json.loads(response.content)
|
||||
self.assertEqual(
|
||||
res_json['enrollment_status'],
|
||||
'Enrollment status for robot-allowed@robot.org: pending'
|
||||
)
|
||||
|
||||
# never enrolled or invited
|
||||
params = {
|
||||
'unique_student_identifier': 'nonotever@example.com'
|
||||
}
|
||||
|
||||
response = self.client.post(url, params)
|
||||
self.assertEqual(response.status_code, 200)
|
||||
res_json = json.loads(response.content)
|
||||
self.assertEqual(
|
||||
res_json['enrollment_status'],
|
||||
'Enrollment status for nonotever@example.com: never enrolled'
|
||||
)
|
||||
|
||||
|
||||
@attr(shard=5)
|
||||
@ddt.ddt
|
||||
|
||||
@@ -104,6 +104,7 @@ from student.models import (
|
||||
UNENROLLED_TO_ENROLLED,
|
||||
UNENROLLED_TO_UNENROLLED,
|
||||
CourseEnrollment,
|
||||
CourseEnrollmentAllowed,
|
||||
EntranceExamConfiguration,
|
||||
ManualEnrollmentAudit,
|
||||
Registration,
|
||||
@@ -1870,6 +1871,63 @@ def get_anon_ids(request, course_id): # pylint: disable=unused-argument
|
||||
@ensure_csrf_cookie
|
||||
@cache_control(no_cache=True, no_store=True, must_revalidate=True)
|
||||
@require_level('staff')
|
||||
@require_post_params(
|
||||
unique_student_identifier="email or username of student for whom to get enrollment status"
|
||||
)
|
||||
def get_student_enrollment_status(request, course_id):
|
||||
"""
|
||||
Get the enrollment status of a student.
|
||||
Limited to staff access.
|
||||
|
||||
Takes query parameter unique_student_identifier
|
||||
"""
|
||||
|
||||
error = ''
|
||||
user = None
|
||||
mode = None
|
||||
is_active = None
|
||||
|
||||
course_id = CourseKey.from_string(course_id)
|
||||
unique_student_identifier = request.POST.get('unique_student_identifier')
|
||||
|
||||
try:
|
||||
user = get_student_from_identifier(unique_student_identifier)
|
||||
mode, is_active = CourseEnrollment.enrollment_mode_for_user(user, course_id)
|
||||
except User.DoesNotExist:
|
||||
# The student could have been invited to enroll without having
|
||||
# registered. We'll also look at CourseEnrollmentAllowed
|
||||
# records, so let the lack of a User slide.
|
||||
pass
|
||||
|
||||
enrollment_status = _('Enrollment status for {student}: unknown').format(student=unique_student_identifier)
|
||||
|
||||
if user and mode:
|
||||
if is_active:
|
||||
enrollment_status = _('Enrollment status for {student}: active').format(student=user)
|
||||
else:
|
||||
enrollment_status = _('Enrollment status for {student}: inactive').format(student=user)
|
||||
else:
|
||||
email = user.email if user else unique_student_identifier
|
||||
allowed = CourseEnrollmentAllowed.may_enroll_and_unenrolled(course_id)
|
||||
if allowed and email in [cea.email for cea in allowed]:
|
||||
enrollment_status = _('Enrollment status for {student}: pending').format(student=email)
|
||||
else:
|
||||
enrollment_status = _('Enrollment status for {student}: never enrolled').format(student=email)
|
||||
|
||||
response_payload = {
|
||||
'course_id': course_id.to_deprecated_string(),
|
||||
'error': error,
|
||||
'enrollment_status': enrollment_status
|
||||
}
|
||||
|
||||
return JsonResponse(response_payload)
|
||||
|
||||
|
||||
@require_POST
|
||||
@ensure_csrf_cookie
|
||||
@cache_control(no_cache=True, no_store=True, must_revalidate=True)
|
||||
@common_exceptions_400
|
||||
@require_level('staff')
|
||||
@require_post_params(
|
||||
unique_student_identifier="email or username of student for whom to get progress url"
|
||||
)
|
||||
|
||||
@@ -22,6 +22,7 @@ urlpatterns = [
|
||||
url(r'^get_sale_order_records$', api.get_sale_order_records, name='get_sale_order_records'),
|
||||
url(r'^sale_validation_url$', api.sale_validation, name='sale_validation'),
|
||||
url(r'^get_anon_ids$', api.get_anon_ids, name='get_anon_ids'),
|
||||
url(r'^get_student_enrollment_status$', api.get_student_enrollment_status, name="get_student_enrollment_status"),
|
||||
url(r'^get_student_progress_url$', api.get_student_progress_url, name='get_student_progress_url'),
|
||||
url(r'^reset_student_attempts$', api.reset_student_attempts, name='reset_student_attempts'),
|
||||
url(r'^rescore_problem$', api.rescore_problem, name='rescore_problem'),
|
||||
|
||||
@@ -565,6 +565,10 @@ def _section_student_admin(course, access):
|
||||
'section_display_name': _('Student Admin'),
|
||||
'access': access,
|
||||
'is_small_course': is_small_course,
|
||||
'get_student_enrollment_status_url': reverse(
|
||||
'get_student_enrollment_status',
|
||||
kwargs={'course_id': unicode(course_key)}
|
||||
),
|
||||
'get_student_progress_url_url': reverse('get_student_progress_url', kwargs={'course_id': unicode(course_key)}),
|
||||
'enrollment_url': reverse('students_update_enrollment', kwargs={'course_id': unicode(course_key)}),
|
||||
'reset_student_attempts_url': reverse('reset_student_attempts', kwargs={'course_id': unicode(course_key)}),
|
||||
|
||||
@@ -32,6 +32,7 @@
|
||||
var studentadmin = this;
|
||||
this.$section = $section;
|
||||
this.$section.data('wrapper', this);
|
||||
this.$field_student_select_enrollment_status = findAndAssert(this.$section, "input[name='student-select-enrollment-status']");
|
||||
this.$field_student_select_progress = findAndAssert(this.$section, "input[name='student-select-progress']");
|
||||
this.$field_student_select_grade = findAndAssert(this.$section, "input[name='student-select-grade']");
|
||||
this.$progress_link = findAndAssert(this.$section, 'a.progress-link');
|
||||
@@ -65,16 +66,51 @@
|
||||
this.$btn_task_history_all = this.$section.find("input[name='task-history-all']");
|
||||
this.$table_task_history_all = this.$section.find('.task-history-all-table');
|
||||
this.instructor_tasks = new (PendingInstructorTasks())(this.$section);
|
||||
this.$request_err = findAndAssert(this.$section, '.student-specific-container .request-response-error');
|
||||
this.$request_err_enrollment_status = findAndAssert(this.$section, '.student-enrollment-status-container .request-response-error');
|
||||
this.$request_err_progress = findAndAssert(this.$section, '.student-progress-container .request-response-error');
|
||||
this.$request_err_grade = findAndAssert(this.$section, '.student-grade-container .request-response-error');
|
||||
this.$request_err_ee = this.$section.find('.entrance-exam-grade-container .request-response-error');
|
||||
this.$request_response_error_all = this.$section.find('.course-specific-container .request-response-error');
|
||||
this.$enrollment_status_link = findAndAssert(this.$section, 'a.enrollment-status-link');
|
||||
this.$enrollment_status = findAndAssert(this.$section, '.student-enrollment-status');
|
||||
this.$enrollment_status_link.click(function(e) {
|
||||
var errorMessage, fullErrorMessage, uniqStudentIdentifier;
|
||||
e.preventDefault();
|
||||
uniqStudentIdentifier = studentadmin.$field_student_select_enrollment_status.val();
|
||||
if (!uniqStudentIdentifier) {
|
||||
studentadmin.$enrollment_status.text('');
|
||||
return studentadmin.$request_err_enrollment_status.text(
|
||||
gettext('Please enter a student email address or username.')
|
||||
);
|
||||
}
|
||||
errorMessage = gettext("Error getting enrollment status for '<%- student_id %>'. Make sure that the student identifier is spelled correctly."); // eslint-disable-line max-len
|
||||
fullErrorMessage = _.template(errorMessage)({
|
||||
student_id: uniqStudentIdentifier
|
||||
});
|
||||
studentadmin.$enrollment_status.text(gettext("Retrieving enrollment status..."));
|
||||
return $.ajax({
|
||||
type: 'POST',
|
||||
dataType: 'json',
|
||||
url: studentadmin.$enrollment_status_link.data('endpoint'),
|
||||
data: {
|
||||
course_id: studentadmin.$enrollment_status_link.data('course-id'),
|
||||
unique_student_identifier: uniqStudentIdentifier
|
||||
},
|
||||
success: studentadmin.clear_errors_then(function(data) {
|
||||
return studentadmin.$enrollment_status.text(data.enrollment_status);
|
||||
}),
|
||||
error: statusAjaxError(function() {
|
||||
studentadmin.$enrollment_status.text('');
|
||||
return studentadmin.$request_err_enrollment_status.text(fullErrorMessage);
|
||||
})
|
||||
});
|
||||
});
|
||||
this.$progress_link.click(function(e) {
|
||||
var errorMessage, fullErrorMessage, uniqStudentIdentifier;
|
||||
e.preventDefault();
|
||||
uniqStudentIdentifier = studentadmin.$field_student_select_progress.val();
|
||||
if (!uniqStudentIdentifier) {
|
||||
return studentadmin.$request_err.text(
|
||||
return studentadmin.$request_err_progress.text(
|
||||
gettext('Please enter a student email address or username.')
|
||||
);
|
||||
}
|
||||
@@ -94,7 +130,7 @@
|
||||
return window.location;
|
||||
}),
|
||||
error: statusAjaxError(function() {
|
||||
return studentadmin.$request_err.text(fullErrorMessage);
|
||||
return studentadmin.$request_err_progress.text(fullErrorMessage);
|
||||
})
|
||||
});
|
||||
});
|
||||
@@ -631,7 +667,8 @@
|
||||
};
|
||||
|
||||
StudentAdmin.prototype.clear_errors_then = function(cb) {
|
||||
this.$request_err.empty();
|
||||
this.$request_err_enrollment_status.empty();
|
||||
this.$request_err_progress.empty();
|
||||
this.$request_err_grade.empty();
|
||||
this.$request_err_ee.empty();
|
||||
this.$request_response_error_all.empty();
|
||||
@@ -641,7 +678,8 @@
|
||||
};
|
||||
|
||||
StudentAdmin.prototype.clear_errors = function() {
|
||||
this.$request_err.empty();
|
||||
this.$request_err_enrollment_status.empty();
|
||||
this.$request_err_progress.empty();
|
||||
this.$request_err_grade.empty();
|
||||
this.$request_err_ee.empty();
|
||||
return this.$request_response_error_all.empty();
|
||||
|
||||
@@ -14,7 +14,29 @@
|
||||
%endif
|
||||
</div>
|
||||
|
||||
<div class="student-specific-container action-type-container">
|
||||
<div class="student-enrollment-status-container action-type-container">
|
||||
<h4 class="hd hd-4">${_("View a specific learner's enrollment status")}</h4>
|
||||
<div class="request-response-error"></div>
|
||||
<label for="student-select-enrollment-status">
|
||||
${_("Learner's {platform_name} email address or username *").format(platform_name=settings.PLATFORM_NAME)}
|
||||
</label>
|
||||
<br>
|
||||
<input type="text" name="student-select-enrollment-status" placeholder="${_('Learner email address or username')}" >
|
||||
|
||||
<blockquote class="student-enrollment-status"></blockquote>
|
||||
|
||||
<br><br>
|
||||
<div class="enrollment-status-link-wrapper">
|
||||
<span name="enrollment-status-link">
|
||||
<a href="" class="enrollment-status-link" data-endpoint="${ section_data['get_student_enrollment_status_url'] }">
|
||||
${_("View Enrollment Status")}
|
||||
</a>
|
||||
</span>
|
||||
</div>
|
||||
<hr>
|
||||
</div>
|
||||
|
||||
<div class="student-progress-container action-type-container">
|
||||
<h4 class="hd hd-4">${_("View a specific learner's grades and progress")}</h4>
|
||||
<div class="request-response-error"></div>
|
||||
<label for="student-select-progress">
|
||||
|
||||
Reference in New Issue
Block a user