Skip to content

Commit

Permalink
feat: Use Provider Onboarding Profile API for Course Onboarding API
Browse files Browse the repository at this point in the history
This pull request makes a change to the method of determining learners' onboarding statuses in a course via the StudentOnboardingStatusByCourseView. Instead of using internal logic to determine onboarding statuses, the provider's onboarding API will be used instead.

MST-761: https://openedx.atlassian.net/browse/MST-761
  • Loading branch information
MichaelRoytman committed Jun 23, 2021
1 parent f6426e9 commit 2e158b7
Show file tree
Hide file tree
Showing 10 changed files with 662 additions and 42 deletions.
5 changes: 5 additions & 0 deletions CHANGELOG.rst
Original file line number Diff line number Diff line change
Expand Up @@ -14,6 +14,11 @@ Change Log
Unreleased
~~~~~~~~~~

[3.17.0] - 2021-06-23
~~~~~~~~~~~~~~~~~~~~~
* Replace internal logic for determing learners' onboarding statuses for the course onboarding API
with provider onboarding API.

[3.16.0] - 2021-06-22
~~~~~~~~~~~~~~~~~~~~~
* Created a GET api endpoint which groups course allowances by users.
Expand Down
2 changes: 1 addition & 1 deletion edx_proctoring/__init__.py
Original file line number Diff line number Diff line change
Expand Up @@ -3,6 +3,6 @@
"""

# Be sure to update the version number in edx_proctoring/package.json
__version__ = '3.16.0'
__version__ = '3.17.0'

default_app_config = 'edx_proctoring.apps.EdxProctoringConfig' # pylint: disable=invalid-name
2 changes: 2 additions & 0 deletions edx_proctoring/constants.py
Original file line number Diff line number Diff line change
Expand Up @@ -70,6 +70,8 @@

ONBOARDING_PROFILE_API = 'edx_proctoring.onboarding_profile_api'

ONBOARDING_PROFILE_INSTRUCTOR_DASHBOARD_API = 'edx_proctoring.onboarding_profile_instructor_dashboard_api'

CONTENT_VIEWABLE_PAST_DUE_DATE = getattr(settings, 'PROCTORED_EXAM_VIEWABLE_PAST_DUE', False)

TIME_MULTIPLIER = 'time_multiplier'
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -3,7 +3,7 @@ edx = edx || {};
(function(Backbone, $, _, gettext) {
'use strict';

var viewHelper, onboardingStatuses, statusAndModeReadableFormat;
var viewHelper, onboardingStatuses, onboardingProfileAPIStatuses, statusAndModeReadableFormat;
edx.instructor_dashboard = edx.instructor_dashboard || {};
edx.instructor_dashboard.proctoring = edx.instructor_dashboard.proctoring || {};
onboardingStatuses = [
Expand All @@ -16,6 +16,14 @@ edx = edx || {};
'rejected',
'error'
];
onboardingProfileAPIStatuses = [
'not_started',
'other_course_approved',
'submitted',
'verified',
'rejected',
'expired'
];
statusAndModeReadableFormat = {
// Onboarding statuses
not_started: gettext('Not Started'),
Expand All @@ -27,6 +35,7 @@ edx = edx || {};
verified: gettext('Verified'),
rejected: gettext('Rejected'),
error: gettext('Error'),
expired: gettext('Expired'),
// TODO: remove as part of MST-745
onboarding_reset_past_due: gettext('Onboarding Reset Failed Due to Past Due Exam'),
// Enrollment modes (Note: 'verified' is both a status and enrollment mode)
Expand Down Expand Up @@ -186,11 +195,21 @@ edx = edx || {};
$searchIcon = $(document.getElementById('onboarding-search-indicator'));
$searchIcon.removeClass('hidden');
},
error: function() {
error: function(unused, response) {
var data, $searchIcon, $spinner, $errorResponse, $onboardingPanel;

// in the case that there is no onboarding data, we
// still want the view to render
var $searchIcon, $spinner;
self.render();

data = $.parseJSON(response.responseText);
if (data.detail) {
$errorResponse = $('#error-response');
$errorResponse.html(data.detail);
$onboardingPanel = $('.onboarding-status-content');
$onboardingPanel.hide();
}

$spinner = $(document.getElementById('onboarding-loading-indicator'));
$spinner.addClass('hidden');
$searchIcon = $(document.getElementById('onboarding-search-indicator'));
Expand All @@ -202,7 +221,8 @@ edx = edx || {};
this.hydrate();
},
render: function() {
var data, dataJson, html, startPage, endPage;
var data, dataJson, html, startPage, endPage, statuses;

if (this.template !== null) {
data = {
previousPage: null,
Expand Down Expand Up @@ -234,12 +254,13 @@ edx = edx || {};
endPage = dataJson.num_pages;
}

statuses = dataJson.use_onboarding_profile_api ? onboardingProfileAPIStatuses : onboardingStatuses;
data = {
previousPage: dataJson.previous,
nextPage: dataJson.next,
currentPage: this.currentPage,
onboardingItems: dataJson.results,
onboardingStatuses: onboardingStatuses,
onboardingStatuses: statuses,
inSearchMode: this.inSearchMode,
searchText: this.searchText,
filters: this.filters,
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -7,6 +7,7 @@ describe('ProctoredExamOnboardingView', function() {
previous: null,
next: null,
num_pages: 1,
use_onboarding_profile_api: false,
results: [
{
username: 'testuser1',
Expand Down Expand Up @@ -40,11 +41,13 @@ describe('ProctoredExamOnboardingView', function() {
previous: null,
next: null,
num_pages: 1,
results: []
results: [],
use_onboarding_profile_api: false
}];

beforeEach(function() {
html = '<div class="wrapper-content wrapper">' +
'<h3 class="error-response" id="error-response"></h3>' +
'<% var isOnboardingItems = onboardingItems.length !== 0 %>' +
'<div class="content onboarding-status-content">' +
'<div class="top-header">' +
Expand Down Expand Up @@ -248,16 +251,39 @@ describe('ProctoredExamOnboardingView', function() {
this.server.respond();
this.server.respond();

this.server.respondWith('GET',
'/api/edx_proctoring/v1/user_onboarding/status/course_id/test_course_id?page=1&statuses=submitted,verified',
[
200,
{
'Content-Type': 'application/json'
},
JSON.stringify(expectedOnboardingDataJson)
]
);

spyOnEvent('.filter-form', 'submit');
$('.status-checkboxes > li > input#submitted').click();
$('.status-checkboxes > li > input#verified').click();
$('.filter-form').submit();

this.server.respond();

expect(this.proctored_exam_onboarding_view.filters).toEqual(['submitted', 'verified']);
expect(this.proctored_exam_onboarding_view.collection.url).toEqual(
'/api/edx_proctoring/v1/user_onboarding/status/course_id/test_course_id?page=1&statuses=submitted,verified'
);

this.server.respondWith('GET', '/api/edx_proctoring/v1/user_onboarding/status/course_id/test_course_id?page=1',
[
200,
{
'Content-Type': 'application/json'
},
JSON.stringify(expectedOnboardingDataJson)
]
);

spyOnEvent('.clear-filters', 'click');
$('.clear-filters').click();

Expand All @@ -277,6 +303,7 @@ describe('ProctoredExamOnboardingView', function() {
JSON.stringify(expectedOnboardingDataJson)
]
);

this.proctored_exam_onboarding_view = new edx.instructor_dashboard.proctoring.ProctoredExamOnboardingView();

this.server.respond();
Expand Down Expand Up @@ -323,7 +350,7 @@ describe('ProctoredExamOnboardingView', function() {
// search for the proctored exam attempt
this.server.respondWith(
'GET',
'/api/edx_proctoring/v1/user_onboarding/status/course_id/test_course_id?text_search=' + searchText,
'/api/edx_proctoring/v1/user_onboarding/status/course_id/test_course_id?page=1&text_search=' + searchText,
[
200,
{
Expand All @@ -333,6 +360,7 @@ describe('ProctoredExamOnboardingView', function() {
]
);


// trigger the search attempt event.
spyOnEvent('.search-onboarding > span.search', 'click');
$('.search-onboarding > span.search').trigger('click');
Expand All @@ -352,4 +380,69 @@ describe('ProctoredExamOnboardingView', function() {
expect(this.proctored_exam_onboarding_view.$el.find('#onboarding-loading-indicator').hasClass('hidden'))
.toEqual(true);
});

it('Renders correct filters for onboarding API', function() {
var onboardingData;

setFixtures(
'<div class="student-onboarding-status-container"' +
'data-course-id="test_course_id">' +
'</div>'
);

onboardingData = JSON.parse(JSON.stringify(expectedOnboardingDataJson));
onboardingData[0].use_onboarding_profile_api = true;

this.server.respondWith('GET', '/api/edx_proctoring/v1/user_onboarding/status/course_id/test_course_id',
[
200,
{
'Content-Type': 'application/json'
},
JSON.stringify(onboardingData)
]
);

this.proctored_exam_onboarding_view = new edx.instructor_dashboard.proctoring.ProctoredExamOnboardingView();

this.server.respond();
this.server.respond();

expect(this.proctored_exam_onboarding_view.$el.find('.status-checkboxes').html())
.toContain('Not Started');
expect(this.proctored_exam_onboarding_view.$el.find('.status-checkboxes').html())
.toContain('Submitted');
expect(this.proctored_exam_onboarding_view.$el.find('.status-checkboxes').html())
.toContain('Verified');
expect(this.proctored_exam_onboarding_view.$el.find('.status-checkboxes').html())
.toContain('Approved in Another Course');
expect(this.proctored_exam_onboarding_view.$el.find('.status-checkboxes').html())
.toContain('Rejected');
expect(this.proctored_exam_onboarding_view.$el.find('.status-checkboxes').html())
.not.toContain('Setup Started');
});

it('renders correctly with 503 response', function() {
var errorMessage;

this.server.respondWith('GET', '/api/edx_proctoring/v1/user_onboarding/status/course_id/test_course_id',
[
503,
{
'Content-Type': 'application/json'
},
JSON.stringify({detail: 'Error message.'})
]
);

this.proctored_exam_onboarding_view = new edx.instructor_dashboard.proctoring.ProctoredExamOnboardingView();

this.server.respond();
this.server.respond();

errorMessage = this.proctored_exam_onboarding_view.$el.find('.error-response');
expect(errorMessage.html()).toContain('Error message.');
expect(errorMessage).toBeVisible();
expect(this.proctored_exam_onboarding_view.$el.find('.onboarding-status-content')).not.toBeVisible();
});
});
Original file line number Diff line number Diff line change
Expand Up @@ -27,11 +27,12 @@
margin-left: 10px;
}

.no-onboarding-data {
.no-onboarding-data, .onboarding-error {
margin: 20px;
font-weight: bold;
}
</style>
<h3 class="error-response" id="error-response"></h3>
<% var isOnboardingItems = onboardingItems.length !== 0 %>
<div class="content onboarding-status-content">
<div class="top-header">
Expand All @@ -45,8 +46,8 @@
<span class="icon fa fa-search" id="onboarding-search-indicator" aria-hidden="true"></span>
<div aria-live="polite" aria-relevant="all">
<div id="onboarding-loading-indicator" class="hidden">
<span class="icon fa fa-spinner fa-pulse" aria-hidden="true"></span>
<span class="sr"><%- gettext("Loading") %></span>
<span class="icon fa fa-spinner fa-pulse" aria-hidden="true"></span>
<span class="sr"><%- gettext("Loading") %></span>
</div>
</div>
</span>
Expand Down Expand Up @@ -78,13 +79,13 @@
<li class="disabled">
<a aria-label="Next">
<span aria-hidden="true">&raquo;</span>
</a>
</a>
</li>
<% } else { %>
<li>
<a class="target-link" href="#" aria-label="Next" data-page-number="<%= currentPage + 1 %>"
>
<span aria-hidden="true">&raquo;</span>
<span aria-hidden="true">&raquo;</span>
</a>
</li>
<% }%>
Expand All @@ -98,7 +99,7 @@
<input type="checkbox" id="<%= status %>" name="status-filters" value="<%= status %>"
<% if (filters.includes(status)) { %>checked="true"<% } %>>
<label for="<%= status %>">
<%- interpolate(gettext(" %(onboardingStatus)s "),
<%- interpolate(gettext(" %(onboardingStatus)s "),
{ onboardingStatus: getReadableString(status) }, true) %>
</label>
</li>
Expand Down Expand Up @@ -126,11 +127,11 @@
<%- interpolate(gettext(" %(username)s "), { username: item.username }, true) %>
</td>
<td>
<%- interpolate(gettext(" %(enrollmentMode)s "),
<%- interpolate(gettext(" %(enrollmentMode)s "),
{ enrollmentMode: getReadableString(item.enrollment_mode) }, true) %>
</td>
<td>
<%- interpolate(gettext(" %(onboardingStatus)s "),
<%- interpolate(gettext(" %(onboardingStatus)s "),
{ onboardingStatus: getReadableString(item.status) }, true) %>
</td>
<td><%= getDateFormat(item.modified) %></td>
Expand Down
Loading

0 comments on commit 2e158b7

Please sign in to comment.