Commit 9d156c04 by Mushtaq Ali Committed by muzaffaryousaf

Remove course wide transcript settings (N/A provider selected) - EDUCATOR-1311

parent ec399645
...@@ -33,6 +33,7 @@ from contentstore.views.videos import KEY_EXPIRATION_IN_SECONDS, StatusDisplaySt ...@@ -33,6 +33,7 @@ from contentstore.views.videos import KEY_EXPIRATION_IN_SECONDS, StatusDisplaySt
from xmodule.modulestore.tests.factories import CourseFactory from xmodule.modulestore.tests.factories import CourseFactory
from openedx.core.djangoapps.profile_images.tests.helpers import make_image_file from openedx.core.djangoapps.profile_images.tests.helpers import make_image_file
from edxval.api import create_or_update_transcript_preferences, get_transcript_preferences
def override_switch(switch, active): def override_switch(switch, active):
...@@ -873,6 +874,18 @@ class TranscriptPreferencesTestCase(VideoUploadTestBase, CourseTestCase): ...@@ -873,6 +874,18 @@ class TranscriptPreferencesTestCase(VideoUploadTestBase, CourseTestCase):
), ),
( (
{ {
'provider': ''
},
'Invalid provider.'
),
(
{
'provider': 'dummy-provider'
},
'Invalid provider.'
),
(
{
'provider': TranscriptProvider.CIELO24 'provider': TranscriptProvider.CIELO24
}, },
'Invalid cielo24 fidelity.' 'Invalid cielo24 fidelity.'
...@@ -970,6 +983,46 @@ class TranscriptPreferencesTestCase(VideoUploadTestBase, CourseTestCase): ...@@ -970,6 +983,46 @@ class TranscriptPreferencesTestCase(VideoUploadTestBase, CourseTestCase):
self.assertEqual(status_code, 200) self.assertEqual(status_code, 200)
self.assertTrue(response['transcript_preferences'], preferences_data) self.assertTrue(response['transcript_preferences'], preferences_data)
def test_remove_transcript_preferences(self):
"""
Test that transcript handler removes transcript preferences correctly.
"""
# First add course wide transcript preferences.
preferences = create_or_update_transcript_preferences(unicode(self.course.id))
# Verify transcript preferences exist
self.assertIsNotNone(preferences)
response = self.client.delete(
self.get_url_for_course_key(self.course.id),
content_type='application/json'
)
self.assertEqual(response.status_code, 204)
# Verify transcript preferences no loger exist
preferences = get_transcript_preferences(unicode(self.course.id))
self.assertIsNone(preferences)
def test_remove_transcript_preferences_not_found(self):
"""
Test that transcript handler works correctly even when no preferences are found.
"""
course_id = 'dummy+course+id'
# Verify transcript preferences do not exist
preferences = get_transcript_preferences(course_id)
self.assertIsNone(preferences)
response = self.client.delete(
self.get_url_for_course_key(course_id),
content_type='application/json'
)
self.assertEqual(response.status_code, 204)
# Verify transcript preferences do not exist
preferences = get_transcript_preferences(course_id)
self.assertIsNone(preferences)
@ddt.data( @ddt.data(
None, None,
{ {
......
...@@ -30,6 +30,7 @@ from edxval.api import ( ...@@ -30,6 +30,7 @@ from edxval.api import (
get_3rd_party_transcription_plans, get_3rd_party_transcription_plans,
get_transcript_preferences, get_transcript_preferences,
create_or_update_transcript_preferences, create_or_update_transcript_preferences,
remove_transcript_preferences,
) )
from opaque_keys.edx.keys import CourseKey from opaque_keys.edx.keys import CourseKey
from openedx.core.djangoapps.waffle_utils import WaffleSwitchNamespace from openedx.core.djangoapps.waffle_utils import WaffleSwitchNamespace
...@@ -327,7 +328,7 @@ def validate_transcript_preferences( ...@@ -327,7 +328,7 @@ def validate_transcript_preferences(
@expect_json @expect_json
@login_required @login_required
@require_POST @require_http_methods(('POST', 'DELETE'))
def transcript_preferences_handler(request, course_key_string): def transcript_preferences_handler(request, course_key_string):
""" """
JSON view handler to post the transcript preferences. JSON view handler to post the transcript preferences.
...@@ -338,26 +339,25 @@ def transcript_preferences_handler(request, course_key_string): ...@@ -338,26 +339,25 @@ def transcript_preferences_handler(request, course_key_string):
Returns: valid json response or 400 with error message Returns: valid json response or 400 with error message
""" """
data = request.json if request.method == 'POST':
provider = data.get('provider', '') data = request.json
provider = data.get('provider')
# TODO: if provider == '': delete course preferences error, preferences = validate_transcript_preferences(
# i.e call delete api end point like delete_transcript_preferences(course_key_string) provider=provider,
cielo24_fidelity=data.get('cielo24_fidelity', ''),
error, preferences = validate_transcript_preferences( cielo24_turnaround=data.get('cielo24_turnaround', ''),
provider=provider, three_play_turnaround=data.get('three_play_turnaround', ''),
cielo24_fidelity=data.get('cielo24_fidelity', ''), preferred_languages=data.get('preferred_languages', [])
cielo24_turnaround=data.get('cielo24_turnaround', ''), )
three_play_turnaround=data.get('three_play_turnaround', ''), if error:
preferred_languages=data.get('preferred_languages', []) response = JsonResponse({'error': error}, status=400)
) else:
preferences.update({'provider': provider})
if error: transcript_preferences = create_or_update_transcript_preferences(course_key_string, **preferences)
response = JsonResponse({'error': error}, status=400) response = JsonResponse({'transcript_preferences': transcript_preferences}, status=200)
else: elif request.method == 'DELETE':
preferences.update({'provider': provider}) remove_transcript_preferences(course_key_string)
transcript_preferences = create_or_update_transcript_preferences(course_key_string, **preferences) response = JsonResponse()
response = JsonResponse({'transcript_preferences': transcript_preferences}, status=200)
return response return response
......
...@@ -186,7 +186,7 @@ define( ...@@ -186,7 +186,7 @@ define(
}) })
); );
// Send successful upload response. // Send successful response.
AjaxHelpers.respondWithJson(requests, { AjaxHelpers.respondWithJson(requests, {
transcript_preferences: activeTranscriptPreferences transcript_preferences: activeTranscriptPreferences
}); });
...@@ -200,6 +200,31 @@ define( ...@@ -200,6 +200,31 @@ define(
); );
}); });
it('removes transcript settings on update settings button click when no provider is selected', function() {
var requests = AjaxHelpers.requests(this);
// Set no provider selected
courseVideoSettingsView.selectedProvider = null;
$courseVideoSettingsEl.find('.action-update-course-video-settings').click();
AjaxHelpers.expectRequest(
requests,
'DELETE',
transcriptPreferencesUrl
);
// Send successful empty content response.
AjaxHelpers.respondWithJson(requests, {});
// Verify that success message is shown.
expect($courseVideoSettingsEl.find('.course-video-settings-message-wrapper.success').html()).toEqual(
'<div class="course-video-settings-message">' +
'<span class="icon fa fa-check-circle" aria-hidden="true"></span>' +
'<span>Settings updated</span>' +
'</div>'
);
});
it('shows error message if server sends error', function() { it('shows error message if server sends error', function() {
var requests = AjaxHelpers.requests(this); var requests = AjaxHelpers.requests(this);
$courseVideoSettingsEl.find('.action-update-course-video-settings').click(); $courseVideoSettingsEl.find('.action-update-course-video-settings').click();
......
...@@ -336,6 +336,43 @@ function($, Backbone, _, gettext, moment, HtmlUtils, StringUtils, TranscriptSett ...@@ -336,6 +336,43 @@ function($, Backbone, _, gettext, moment, HtmlUtils, StringUtils, TranscriptSett
); );
}, },
updateSuccessResponseStatus: function(data) {
this.renderResponseStatus(gettext('Settings updated'), 'success');
// Sync ActiveUploadListView with latest active plan.
this.activeTranscriptionPlan = data;
Backbone.trigger('coursevideosettings:syncActiveTranscriptPreferences', this.activeTranscriptionPlan);
},
updateFailResponseStatus: function(data) {
var errorMessage;
// Enclose inside try-catch so that if we get erroneous data, we could still
// show some error to user
try {
errorMessage = $.parseJSON(data).error;
} catch (e) {} // eslint-disable-line no-empty
errorMessage = errorMessage || gettext('Error saving data');
this.renderResponseStatus(errorMessage, 'error');
},
renderResponseStatus: function(responseText, type) {
var addClass = type === 'error' ? 'error' : 'success',
removeClass = type === 'error' ? 'success' : 'error',
iconClass = type === 'error' ? 'fa-info-circle' : 'fa-check-circle',
$messageWrapperEl = this.$el.find('.course-video-settings-message-wrapper');
$messageWrapperEl.removeClass(removeClass);
$messageWrapperEl.addClass(addClass);
HtmlUtils.setHtml(
$messageWrapperEl,
HtmlUtils.interpolateHtml(
HtmlUtils.HTML('<div class="course-video-settings-message"><span class="icon fa {iconClass}" aria-hidden="true"></span><span>{text}</span></div>'), // eslint-disable-line max-len
{
text: responseText,
iconClass: iconClass
}
)
);
},
clearResponseStatus: function() { clearResponseStatus: function() {
// Remove parent level state. // Remove parent level state.
var $messageWrapperEl = this.$el.find('.course-video-settings-message-wrapper'); var $messageWrapperEl = this.$el.find('.course-video-settings-message-wrapper');
...@@ -407,59 +444,38 @@ function($, Backbone, _, gettext, moment, HtmlUtils, StringUtils, TranscriptSett ...@@ -407,59 +444,38 @@ function($, Backbone, _, gettext, moment, HtmlUtils, StringUtils, TranscriptSett
saveTranscriptPreferences: function() { saveTranscriptPreferences: function() {
var self = this, var self = this,
$messageWrapperEl = self.$el.find('.course-video-settings-message-wrapper'); responseTranscriptPreferences;
// First clear response status if present already // First clear response status if present already
this.clearResponseStatus(); this.clearResponseStatus();
$.postJSON(this.transcriptHandlerUrl, { if (self.selectedProvider) {
provider: self.selectedProvider, $.postJSON(self.transcriptHandlerUrl, {
cielo24_fidelity: self.selectedFidelityPlan, provider: self.selectedProvider,
cielo24_turnaround: self.selectedProvider === CIELO24 ? self.selectedTurnaroundPlan : '', cielo24_fidelity: self.selectedFidelityPlan,
three_play_turnaround: self.selectedProvider === THREE_PLAY_MEDIA ? self.selectedTurnaroundPlan : '', cielo24_turnaround: self.selectedProvider === CIELO24 ? self.selectedTurnaroundPlan : '',
preferred_languages: self.selectedLanguages, three_play_turnaround: self.selectedProvider === THREE_PLAY_MEDIA ? self.selectedTurnaroundPlan : '', // eslint-disable-line max-len
global: false // Do not trigger global AJAX error handler preferred_languages: self.selectedLanguages,
}, function(data) { global: false // Do not trigger global AJAX error handler
if (data.transcript_preferences) { }, function(data) {
$messageWrapperEl.removeClass('error'); responseTranscriptPreferences = data ? data.transcript_preferences : null;
$messageWrapperEl.addClass('success'); self.updateSuccessResponseStatus(responseTranscriptPreferences);
HtmlUtils.setHtml( }).fail(function(jqXHR) {
$messageWrapperEl, if (jqXHR.responseText) {
HtmlUtils.interpolateHtml( self.updateFailResponseStatus(jqXHR.responseText);
HtmlUtils.HTML('<div class="course-video-settings-message"><span class="icon fa fa-check-circle" aria-hidden="true"></span><span>{text}</span></div>'), // eslint-disable-line max-len }
{ });
text: gettext('Settings updated') } else {
} $.ajax({
) type: 'DELETE',
); url: self.transcriptHandlerUrl
self.activeTranscriptionPlan = data.transcript_preferences; }).done(function() {
self.updateSuccessResponseStatus(null);
// Sync ActiveUploadListView with latest active plan. }).fail(function(jqXHR) {
Backbone.trigger( if (jqXHR.responseText) {
'coursevideosettings:syncActiveTranscriptPreferences', self.updateFailResponseStatus(jqXHR.responseText);
self.activeTranscriptionPlan }
); });
} }
}).fail(function(jqXHR) {
var errorMessage;
if (jqXHR.responseText) {
// Enclose inside try-catch so that if we get erroneous data, we could still show some error to user
try {
errorMessage = $.parseJSON(jqXHR.responseText).error;
} catch (e) {} // eslint-disable-line no-empty
$messageWrapperEl.removeClass('success');
$messageWrapperEl.addClass('error');
HtmlUtils.setHtml(
$messageWrapperEl,
HtmlUtils.interpolateHtml(
HtmlUtils.HTML('<div class="course-video-settings-message"><span class="icon fa fa-info-circle" aria-hidden="true"></span><span>{text}</span></div>'), // eslint-disable-line max-len
{
text: errorMessage || gettext('Error saving data')
}
)
);
}
});
}, },
updateCourseVideoSettings: function() { updateCourseVideoSettings: function() {
...@@ -489,35 +505,29 @@ function($, Backbone, _, gettext, moment, HtmlUtils, StringUtils, TranscriptSett ...@@ -489,35 +505,29 @@ function($, Backbone, _, gettext, moment, HtmlUtils, StringUtils, TranscriptSett
}, },
setFixedCourseVideoSettingsPane: function() { setFixedCourseVideoSettingsPane: function() {
var windowWidth = $(window).width(), var $courseVideoSettingsButton = $('.course-video-settings-button'),
windowHeight = $(window).height(),
$courseVideoSettingsButton = $('.course-video-settings-button'),
$courseVideoSettingsContainer = this.$el.find('.course-video-settings-container'), $courseVideoSettingsContainer = this.$el.find('.course-video-settings-container'),
initialPositionTop = $courseVideoSettingsContainer.offset().top, initialPositionTop = $courseVideoSettingsContainer.offset().top,
courseVideoSettingsButtonLeft = $courseVideoSettingsButton.offset().left, // Button right position = width of window - button left position - button width - paddings - border.
fixedOffsetRight = windowWidth - $courseVideoSettingsButtonRight = $(window).width() -
courseVideoSettingsButtonLeft - $courseVideoSettingsButton.width() - 25; $courseVideoSettingsButton.offset().left -
$courseVideoSettingsButton.width() -
$courseVideoSettingsButton.css('padding-left').replace('px', '') -
$courseVideoSettingsButton.css('padding-right').replace('px', '') -
$courseVideoSettingsButton.css('border-width').replace('px', '') - 5; // Extra pixles for slack;
// set windows total height // Set to windows total height
$courseVideoSettingsContainer.css('height', windowHeight); $courseVideoSettingsContainer.css('height', $(window).height());
$courseVideoSettingsContainer.css('right', 20);
// Start settings pane adjascent to 'course video settings' button.
$courseVideoSettingsContainer.css('right', $courseVideoSettingsButtonRight);
// Make sticky when scroll reaches top. // Make sticky when scroll reaches top.
$(window).scroll(function() { $(window).scroll(function() {
// Remove transition when we start scrolling.
// Why we do this? The settings pane come back and forth when it is switched between
// position:fixed and position:absolute, it's right and top position are then being changed wrt to their
// position layout.
$courseVideoSettingsContainer.css('transition', 'none');
if ($(window).scrollTop() >= initialPositionTop) { if ($(window).scrollTop() >= initialPositionTop) {
$courseVideoSettingsContainer.addClass('fixed-container'); $courseVideoSettingsContainer.addClass('fixed-container');
// TODO: Removes these js calculations and try to do through CSS way.
$courseVideoSettingsContainer.css('right', fixedOffsetRight);
} else { } else {
$courseVideoSettingsContainer.removeClass('fixed-container'); $courseVideoSettingsContainer.removeClass('fixed-container');
$courseVideoSettingsContainer.css('right', 20);
} }
}); });
}, },
......
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