feat: add support for user feedback on autogenerated transcripts (#33518)
* feat: WIP transcript feedback * feat: Add UI mock for Transcript Feedbacks (#33416) * feat: Add UI mock for Transcript Feedbacks * fix: Fix mongo tests * feat: Get video_uuid, user_uuid and language for request (#33445) * feat: make call to ai-translations to obtain feedback * feat: Show widget if transcript was AI generated * feat: bind all class methods * fix: async calls * feat: send request when choosing feedback * feat: update showing condition (#33474) * fix: ajax success lint * fix: video caption specs errors fixed * feat: add coverage to feedback widget * chore: connect XT to LMS and CMS * feat: use url * chore: add vars to devstack * chore: fix url name * feat: update unit tests regarding env vars * fix: fix test_video_mongo * feat: add more tests * feat: remove console log Co-authored-by: Jesper Hodge <19345795+jesperhodge@users.noreply.github.com> * fix: rename shouldShowWidget to loadAndSetVisibility --------- Co-authored-by: María Guillermina Véscovo <mvescovo@2u.com> Co-authored-by: Jesper Hodge <19345795+jesperhodge@users.noreply.github.com>
This commit is contained in:
247
xmodule/js/src/video/037_video_transcript_feedback.js
Normal file
247
xmodule/js/src/video/037_video_transcript_feedback.js
Normal file
@@ -0,0 +1,247 @@
|
||||
(function(define) {
|
||||
// VideoTranscriptFeedbackHandler module.
|
||||
|
||||
'use strict';
|
||||
|
||||
define('video/037_video_caption.js', ['underscore'],
|
||||
function(_) {
|
||||
/**
|
||||
* @desc VideoTranscriptFeedbackHandler module exports a function.
|
||||
*
|
||||
* @type {function}
|
||||
* @access public
|
||||
*
|
||||
* @param {object} state - The object containing the state of the video
|
||||
* player. All other modules, their parameters, public variables, etc.
|
||||
* are available via this object.
|
||||
*
|
||||
* @this {object} The global window object.
|
||||
*
|
||||
*/
|
||||
|
||||
var VideoTranscriptFeedbackHandler = function(state) {
|
||||
if (!(this instanceof VideoTranscriptFeedbackHandler)) {
|
||||
return new VideoTranscriptFeedbackHandler(state);
|
||||
}
|
||||
|
||||
_.bindAll(this, 'destroy', 'getFeedbackForCurrentTranscript', 'markAsPositiveFeedback', 'markAsNegativeFeedback', 'markAsEmptyFeedback',
|
||||
'selectThumbsUp', 'selectThumbsDown', 'unselectThumbsUp', 'unselectThumbsDown', 'thumbsUpClickHandler', 'thumbsDownClickHandler',
|
||||
'sendFeedbackForCurrentTranscript', 'onHideLanguageMenu', 'getCurrentLanguage', 'loadAndSetVisibility', 'showWidget', 'hideWidget'
|
||||
);
|
||||
|
||||
this.state = state;
|
||||
this.state.videoTranscriptFeedback = this;
|
||||
this.currentTranscriptLanguage = this.state.lang;
|
||||
this.transcriptLanguages = this.state.config.transcriptLanguages;
|
||||
|
||||
if (this.state.el.find('.wrapper-transcript-feedback').length) {
|
||||
this.initialize();
|
||||
}
|
||||
|
||||
return false;
|
||||
};
|
||||
|
||||
VideoTranscriptFeedbackHandler.prototype = {
|
||||
|
||||
destroy: function() {
|
||||
this.state.el.off(this.events);
|
||||
},
|
||||
|
||||
// Initializes the module.
|
||||
initialize: function() {
|
||||
this.el = this.state.el.find('.wrapper-transcript-feedback');
|
||||
|
||||
this.videoId = this.el.data('video-id');
|
||||
this.userId = this.el.data('user-id');
|
||||
this.aiTranslationsUrl = this.state.config.aiTranslationsUrl;
|
||||
|
||||
this.thumbsUpButton = this.el.find('.thumbs-up-btn');
|
||||
this.thumbsDownButton = this.el.find('.thumbs-down-btn');
|
||||
this.thumbsUpButton.on('click', this.thumbsUpClickHandler);
|
||||
this.thumbsDownButton.on('click', this.thumbsDownClickHandler);
|
||||
|
||||
this.events = {
|
||||
'language_menu:hide': this.onHideLanguageMenu,
|
||||
destroy: this.destroy
|
||||
};
|
||||
this.loadAndSetVisibility();
|
||||
this.bindHandlers();
|
||||
},
|
||||
|
||||
bindHandlers: function() {
|
||||
this.state.el.on(this.events);
|
||||
},
|
||||
|
||||
getFeedbackForCurrentTranscript: function() {
|
||||
var self = this;
|
||||
var url = self.aiTranslationsUrl + '/transcript-feedback' + '?transcript_language=' + self.currentTranscriptLanguage + '&video_uuid=' + self.videoId + '&user_id=' + self.userId;
|
||||
|
||||
$.ajax({
|
||||
url: url,
|
||||
type: 'GET',
|
||||
success: function(data) {
|
||||
if (data && data.value === true) {
|
||||
self.markAsPositiveFeedback();
|
||||
self.currentFeedback = true;
|
||||
} else {
|
||||
if (data && data.value === false) {
|
||||
self.markAsNegativeFeedback();
|
||||
self.currentFeedback = false;
|
||||
} else {
|
||||
self.markAsEmptyFeedback();
|
||||
self.currentFeedback = null;
|
||||
}
|
||||
}
|
||||
},
|
||||
error: function(error) {
|
||||
self.markAsEmptyFeedback();
|
||||
self.currentFeedback = null;
|
||||
}
|
||||
});
|
||||
},
|
||||
|
||||
markAsPositiveFeedback: function() {
|
||||
this.selectThumbsUp();
|
||||
this.unselectThumbsDown();
|
||||
},
|
||||
|
||||
markAsNegativeFeedback: function() {
|
||||
this.selectThumbsDown();
|
||||
this.unselectThumbsUp();
|
||||
},
|
||||
|
||||
markAsEmptyFeedback: function() {
|
||||
this.unselectThumbsUp();
|
||||
this.unselectThumbsDown();
|
||||
},
|
||||
|
||||
selectThumbsUp: function() {
|
||||
var thumbsUpIcon = this.thumbsUpButton.find('.thumbs-up-icon');
|
||||
if (thumbsUpIcon[0].classList.contains('fa-thumbs-o-up')) {
|
||||
thumbsUpIcon[0].classList.remove("fa-thumbs-o-up");
|
||||
thumbsUpIcon[0].classList.add("fa-thumbs-up");
|
||||
}
|
||||
},
|
||||
|
||||
selectThumbsDown: function() {
|
||||
var thumbsDownIcon = this.thumbsDownButton.find('.thumbs-down-icon');
|
||||
if (thumbsDownIcon[0].classList.contains('fa-thumbs-o-down')) {
|
||||
thumbsDownIcon[0].classList.remove("fa-thumbs-o-down");
|
||||
thumbsDownIcon[0].classList.add("fa-thumbs-down");
|
||||
}
|
||||
},
|
||||
|
||||
unselectThumbsUp: function() {
|
||||
var thumbsUpIcon = this.thumbsUpButton.find('.thumbs-up-icon');
|
||||
if (thumbsUpIcon[0].classList.contains('fa-thumbs-up')) {
|
||||
thumbsUpIcon[0].classList.remove("fa-thumbs-up");
|
||||
thumbsUpIcon[0].classList.add("fa-thumbs-o-up");
|
||||
}
|
||||
},
|
||||
|
||||
unselectThumbsDown: function() {
|
||||
var thumbsDownIcon = this.thumbsDownButton.find('.thumbs-down-icon');
|
||||
if (thumbsDownIcon[0].classList.contains('fa-thumbs-down')) {
|
||||
thumbsDownIcon[0].classList.remove("fa-thumbs-down");
|
||||
thumbsDownIcon[0].classList.add("fa-thumbs-o-down");
|
||||
}
|
||||
},
|
||||
|
||||
thumbsUpClickHandler: function() {
|
||||
if (this.currentFeedback) {
|
||||
this.sendFeedbackForCurrentTranscript(null);
|
||||
} else {
|
||||
this.sendFeedbackForCurrentTranscript(true);
|
||||
}
|
||||
},
|
||||
|
||||
thumbsDownClickHandler: function() {
|
||||
if (this.currentFeedback === false) {
|
||||
this.sendFeedbackForCurrentTranscript(null);
|
||||
} else {
|
||||
this.sendFeedbackForCurrentTranscript(false);
|
||||
}
|
||||
},
|
||||
|
||||
sendFeedbackForCurrentTranscript: function(feedbackValue) {
|
||||
var self = this;
|
||||
var url = self.aiTranslationsUrl + '/transcript-feedback/';
|
||||
$.ajax({
|
||||
url: url,
|
||||
type: 'POST',
|
||||
dataType: 'json',
|
||||
data: {
|
||||
transcript_language: self.currentTranscriptLanguage,
|
||||
video_uuid: self.videoId,
|
||||
user_id: self.userId,
|
||||
value: feedbackValue,
|
||||
},
|
||||
success: function(data) {
|
||||
if (data && data.value === true) {
|
||||
self.markAsPositiveFeedback();
|
||||
self.currentFeedback = true;
|
||||
} else {
|
||||
if (data && data.value === false) {
|
||||
self.markAsNegativeFeedback();
|
||||
self.currentFeedback = false;
|
||||
} else {
|
||||
self.markAsEmptyFeedback();
|
||||
self.currentFeedback = null;
|
||||
}
|
||||
}
|
||||
},
|
||||
error: function() {
|
||||
self.markAsEmptyFeedback();
|
||||
self.currentFeedback = null;
|
||||
}
|
||||
});
|
||||
},
|
||||
|
||||
onHideLanguageMenu: function() {
|
||||
var newLanguageSelected = this.getCurrentLanguage();
|
||||
if (this.currentTranscriptLanguage !== newLanguageSelected) {
|
||||
this.currentTranscriptLanguage = this.getCurrentLanguage();
|
||||
this.loadAndSetVisibility();
|
||||
}
|
||||
},
|
||||
|
||||
getCurrentLanguage: function() {
|
||||
var language = this.state.lang;
|
||||
return language;
|
||||
},
|
||||
|
||||
loadAndSetVisibility: function() {
|
||||
var self = this;
|
||||
var url = self.aiTranslationsUrl + '/video-transcript' + '?transcript_language=' + self.currentTranscriptLanguage + '&video_uuid=' + self.videoId;
|
||||
|
||||
$.ajax({
|
||||
url: url,
|
||||
type: 'GET',
|
||||
async: false,
|
||||
success: function(data) {
|
||||
if (data && data.status === 'Completed') {
|
||||
self.showWidget();
|
||||
self.getFeedbackForCurrentTranscript();
|
||||
} else {
|
||||
self.hideWidget();
|
||||
}
|
||||
},
|
||||
error: function(error) {
|
||||
self.hideWidget();
|
||||
}
|
||||
});
|
||||
},
|
||||
|
||||
showWidget: function() {
|
||||
this.el.show();
|
||||
},
|
||||
|
||||
hideWidget: function() {
|
||||
this.el.hide();
|
||||
},
|
||||
|
||||
};
|
||||
|
||||
return VideoTranscriptFeedbackHandler;
|
||||
});
|
||||
}(RequireJS.define));
|
||||
@@ -65,14 +65,15 @@
|
||||
'video/09_completion.js',
|
||||
'video/10_commands.js',
|
||||
'video/095_video_context_menu.js',
|
||||
'video/036_video_social_sharing.js'
|
||||
'video/036_video_social_sharing.js',
|
||||
'video/037_video_transcript_feedback.js'
|
||||
],
|
||||
function(
|
||||
VideoStorage, initialize, FocusGrabber, VideoAccessibleMenu, VideoControl, VideoFullScreen,
|
||||
VideoQualityControl, VideoProgressSlider, VideoVolumeControl, VideoSpeedControl, VideoAutoAdvanceControl,
|
||||
VideoCaption, VideoPlayPlaceholder, VideoPlayPauseControl, VideoPlaySkipControl, VideoSkipControl,
|
||||
VideoBumper, VideoSaveStatePlugin, VideoEventsPlugin, VideoEventsBumperPlugin, VideoPoster,
|
||||
VideoCompletionHandler, VideoCommands, VideoContextMenu, VideoSocialSharing
|
||||
VideoCompletionHandler, VideoCommands, VideoContextMenu, VideoSocialSharing, VideoTranscriptFeedback
|
||||
) {
|
||||
/* RequireJS */
|
||||
var youtubeXhr = null,
|
||||
@@ -92,10 +93,10 @@
|
||||
FocusGrabber, VideoControl, VideoPlayPlaceholder,
|
||||
VideoPlayPauseControl, VideoProgressSlider, VideoSpeedControl,
|
||||
VideoVolumeControl, VideoQualityControl, VideoFullScreen, VideoCaption, VideoCommands,
|
||||
VideoContextMenu, VideoSaveStatePlugin, VideoEventsPlugin, VideoCompletionHandler
|
||||
VideoContextMenu, VideoSaveStatePlugin, VideoEventsPlugin, VideoCompletionHandler, VideoTranscriptFeedback
|
||||
].concat(autoAdvanceEnabled ? [VideoAutoAdvanceControl] : []),
|
||||
bumperVideoModules = [VideoControl, VideoPlaySkipControl, VideoSkipControl,
|
||||
VideoVolumeControl, VideoCaption, VideoCommands, VideoSaveStatePlugin,
|
||||
VideoVolumeControl, VideoCaption, VideoCommands, VideoSaveStatePlugin, VideoTranscriptFeedback,
|
||||
VideoEventsBumperPlugin, VideoCompletionHandler],
|
||||
state = {
|
||||
el: el,
|
||||
|
||||
Reference in New Issue
Block a user