diff --git a/app/assets/javascripts/editor/editor.js.erb b/app/assets/javascripts/editor/editor.js.erb index 2c2b8011..9deff0d6 100644 --- a/app/assets/javascripts/editor/editor.js.erb +++ b/app/assets/javascripts/editor/editor.js.erb @@ -323,9 +323,7 @@ configureEditors: function () { var button = $('#requestComments'); button.prop('disabled', true); button.on('click', function () { - if ($('#editor').data('show-interventions') == true){ - $('#rfc_intervention_text').hide() - } + $('#rfc_intervention_text').hide() $('#comment-modal').modal('show'); }); @@ -578,64 +576,63 @@ configureEditors: function () { * interventions * */ initializeInterventionTimer: function() { - $.ajax({ - data: { - exercise_id: $('#editor').data('exercise-id'), - user_id: $('#editor').data('user-id') - }, - dataType: 'json', - method: 'GET', - // get working times for this exercise - url: $('#editor').data('working-times-url'), - success: function (data) { - var percentile75 = data['working_time_75_percentile']; - var accumulatedWorkTimeUser = data['working_time_accumulated']; + if ($('#editor').data('rfc-interventions') == true || $('#editor').data('break-interventions') == true) { // split in break or rfc intervention + $.ajax({ + data: { + exercise_id: $('#editor').data('exercise-id'), + user_id: $('#editor').data('user-id') + }, + dataType: 'json', + method: 'GET', + // get working times for this exercise + url: $('#editor').data('working-times-url'), + success: function (data) { + var percentile75 = data['working_time_75_percentile']; + var accumulatedWorkTimeUser = data['working_time_accumulated']; - var timeUntilBreak = 20 * 60 * 1000; - var minTimeUntilAskQuestion = 15 * 60 * 1000; + var minTimeIntervention = 10 * 60 * 1000; - if ((accumulatedWorkTimeUser - percentile75) > 0) { - // working time is already over 75 percentile - var timeUntilAskQuestion = minTimeUntilAskQuestion; - } else { - // working time is less than 75 percentile - // ensure we give user at least 10 minutes before we bother the user - var timeUntilAskForRFC = (percentile75 - accumulatedWorkTimeUser) > minTimeUntilAskQuestion ? (percentile75 - accumulatedWorkTimeUser) : minTimeUntilAskQuestion; + if ((accumulatedWorkTimeUser - percentile75) > 0) { + // working time is already over 75 percentile + var timeUntilIntervention = minTimeIntervention; + } else { + // working time is less than 75 percentile + // ensure we give user at least minTimeIntervention before we bother the user + var timeUntilIntervention = Math.max(percentile75 - accumulatedWorkTimeUser, minTimeIntervention); + } + if ($('#editor').data('break-interventions')){ + setTimeout(function () { + $('#break-intervention-modal').modal('show'); + $.ajax({ + data: { + intervention_type: 'BreakIntervention' + }, + dataType: 'json', + type: 'POST', + url: $('#editor').data('intervention-save-url') + }); + }, timeUntilIntervention); + } else if ($('#editor').data('rfc-interventions')) { + setTimeout(function () { + var button = $('#requestComments'); + // only show intervention if user did not requested for a comment already + if (!button.prop('disabled')) { + $('#rfc_intervention_text').show(); + $('#comment-modal').modal('show'); + $.ajax({ + data: { + intervention_type: 'QuestionIntervention' + }, + dataType: 'json', + type: 'POST', + url: $('#editor').data('intervention-save-url') + }); + }; + }, timeUntilIntervention); + } } - - // if notifications are too close to each other, ensure some time differences between them - if (Math.abs(timeUntilAskForRFC - timeUntilBreak) < 5 * 1000 * 60){ - timeUntilBreak = timeUntilBreak * 2; - } - - setTimeout(function() { - $('#break-intervention-modal').modal('show'); - $.ajax({ - data: { - intervention_type: 'BreakIntervention' - }, - dataType: 'json', - type: 'POST', - url: $('#editor').data('intervention-save-url')}); - }, timeUntilBreak); - - - setTimeout(function() { - var button = $('#requestComments'); - if (!button.prop('disabled')){ - $('#rfc_intervention_text').show(); - $('#comment-modal').modal('show'); - $.ajax({ - data: { - intervention_type: 'QuestionIntervention' - }, - dataType: 'json', - type: 'POST', - url: $('#editor').data('intervention-save-url')}); - }; - }, timeUntilAskForRFC); - } - }); + }); + } }, initializeSearchButton: function(){ @@ -671,9 +668,7 @@ configureEditors: function () { this.initializeDescriptionToggle(); this.initializeSideBarTooltips(); this.initializeTooltips(); - if ($('#editor').data('show-interventions') == true){ - this.initializeInterventionTimer(); - } + this.initializeInterventionTimer(); this.initializeSearchButton(); this.initPrompt(); this.renderScore(); diff --git a/app/controllers/exercises_controller.rb b/app/controllers/exercises_controller.rb index 25361125..4a643a5d 100644 --- a/app/controllers/exercises_controller.rb +++ b/app/controllers/exercises_controller.rb @@ -21,7 +21,7 @@ class ExercisesController < ApplicationController private :authorize! def max_intervention_count - 3 + 2 end @@ -168,7 +168,15 @@ class ExercisesController < ApplicationController user_got_enough_interventions = UserExerciseIntervention.where(exercise: @exercise, user: current_user).count >= max_intervention_count is_java_course = @course_token && @course_token.eql?(java_course_token) - @show_interventions = (!is_java_course || user_got_enough_interventions) ? "false" : "true" + user_intervention_group = UserGroupSeparator.getInterventionGroup(current_user) + + case user_intervention_group + when :no_intervention + when :break_intervention + @show_break_interventions = (!is_java_course || user_got_enough_interventions) ? "false" : "true" + when :rfc_intervention + @show_rfc_interventions = (!is_java_course || user_got_enough_interventions) ? "false" : "true" + end @search = Search.new @search.exercise = @exercise diff --git a/app/models/proxy_exercise.rb b/app/models/proxy_exercise.rb index 11b5a545..a425b91b 100644 --- a/app/models/proxy_exercise.rb +++ b/app/models/proxy_exercise.rb @@ -53,28 +53,43 @@ class ProxyExercise < ActiveRecord::Base end def find_matching_exercise(user) - exercises_user_has_accessed = user.submissions.where("cause IN ('submit','assess')").map{|s| s.exercise}.uniq - tags_user_has_seen = exercises_user_has_accessed.map{|ex| ex.tags}.uniq.flatten - Rails.logger.debug("exercises_user_has_accessed #{exercises_user_has_accessed.map{|e|e.id}.join(",")}") + user_group = UserGroupSeparator.getProxyExerciseGroup(user) + case user_group + when :dummy_assigment + rec_ex = select_easiest_exercise(exercises) + @reason[:reason] = "dummy group" + Rails.logger.debug("assigned user to dummy group, and gave him exercise: #{rec_ex.title}") + rec_ex + when :random_assigment + @reason[:reason] = "random group" + rec_ex = exercises.shuffle.first + Rails.logger.debug("assigned user to random group, and gave him exercise: #{rec_ex.title}") + rec_ex + when :recommended_assignment + exercises_user_has_accessed = user.submissions.where("cause IN ('submit','assess')").map{|s| s.exercise}.uniq.compact + tags_user_has_seen = exercises_user_has_accessed.map{|ex| ex.tags}.uniq.flatten + Rails.logger.debug("exercises_user_has_accessed #{exercises_user_has_accessed.map{|e|e.id}.join(",")}") - # find execises - potential_recommended_exercises = [] - exercises.each do |ex| - ## find exercises which have only tags the user has already seen - if (ex.tags - tags_user_has_seen).empty? - potential_recommended_exercises << ex - end - end - Rails.logger.debug("potential_recommended_exercises: #{potential_recommended_exercises.map{|e|e.id}}") - # if all exercises contain tags which the user has never seen, recommend easiest exercise - if potential_recommended_exercises.empty? - Rails.logger.debug("matched easiest exercise in pool") - @reason[:reason] = "easiest exercise in pool. empty potential exercises" - select_easiest_exercise(exercises) - else - recommended_exercise = select_best_matching_exercise(user, exercises_user_has_accessed, potential_recommended_exercises) - recommended_exercise + # find execises + potential_recommended_exercises = [] + exercises.each do |ex| + ## find exercises which have only tags the user has already seen + if (ex.tags - tags_user_has_seen).empty? + potential_recommended_exercises << ex + end + end + Rails.logger.debug("potential_recommended_exercises: #{potential_recommended_exercises.map{|e|e.id}}") + # if all exercises contain tags which the user has never seen, recommend easiest exercise + if potential_recommended_exercises.empty? + Rails.logger.debug("matched easiest exercise in pool") + @reason[:reason] = "easiest exercise in pool. empty potential exercises" + select_easiest_exercise(exercises) + else + recommended_exercise = select_best_matching_exercise(user, exercises_user_has_accessed, potential_recommended_exercises) + recommended_exercise + end end + end private :find_matching_exercise diff --git a/app/views/exercises/_editor.html.slim b/app/views/exercises/_editor.html.slim index 5b3c3478..4291028f 100644 --- a/app/views/exercises/_editor.html.slim +++ b/app/views/exercises/_editor.html.slim @@ -1,8 +1,9 @@ - external_user_external_id = @current_user.respond_to?(:external_id) ? @current_user.external_id : '' #'tests' #(@current_user.uuid.present? ? @current_user.uuid : '') - external_user_id = @current_user.respond_to?(:external_id) ? @current_user.id : '' #'tests' #(@current_user.uuid.present? ? @current_user.uuid : '') - consumer_id = @current_user.respond_to?(:external_id) ? @current_user.consumer_id : '' #'tests' #(@current_user.uuid.present? ? @current_user.uuid : '') -- show_interventions = @show_interventions || "false" -#editor.row data-exercise-id=exercise.id data-message-depleted=t('exercises.editor.depleted') data-message-timeout=t('exercises.editor.timeout', permitted_execution_time: @exercise.execution_environment.permitted_execution_time) data-errors-url=execution_environment_errors_path(exercise.execution_environment) data-submissions-url=submissions_path data-user-id=@current_user.id data-user-external-id=external_user_external_id data-working-times-url=working_times_exercise_path data-intervention-save-url=intervention_exercise_path data-show-interventions=show_interventions data-course_token=@course_token data-search-save-url=search_exercise_path +- show_break_interventions = @show_break_interventions || "false" +- show_rfc_interventions = @show_rfc_interventions || "false" +#editor.row data-exercise-id=exercise.id data-message-depleted=t('exercises.editor.depleted') data-message-timeout=t('exercises.editor.timeout', permitted_execution_time: @exercise.execution_environment.permitted_execution_time) data-errors-url=execution_environment_errors_path(exercise.execution_environment) data-submissions-url=submissions_path data-user-id=@current_user.id data-user-external-id=external_user_external_id data-working-times-url=working_times_exercise_path data-intervention-save-url=intervention_exercise_path data-rfc-interventions=show_rfc_interventions data-break-interventions=show_break_interventions data-course_token=@course_token data-search-save-url=search_exercise_path div id="sidebar" class=(@exercise.hide_file_tree ? 'sidebar-col-collapsed' : 'sidebar-col') = render('editor_file_tree', exercise: @exercise, files: @files) div id='output_sidebar' class='output-col-collapsed' = render('exercises/editor_output', external_user_id: external_user_id, consumer_id: consumer_id ) div id='frames' class='editor-col' diff --git a/lib/user_group_separator.rb b/lib/user_group_separator.rb new file mode 100644 index 00000000..320cce96 --- /dev/null +++ b/lib/user_group_separator.rb @@ -0,0 +1,27 @@ +class UserGroupSeparator + + # seperates user into 30% no intervention, 30% break intervention, 40% rfc intervention + def self.getInterventionGroup(user) + lastDigitId = user.id % 10 + if lastDigitId < 3 # 0,1,2 + :no_intervention + elsif lastDigitId < 6 # 3,4,5 + :break_intervention + else # 6,7,8,9 + :rfc_intervention + end + end + + # seperates user into 20% dummy assignment, 20% random assignemnt, 60% recommended assignment + def self.getProxyExerciseGroup(user) + lastDigitCreatedAt = user.created_at.to_i % 10 + if lastDigitCreatedAt < 2 # 0,1 + :dummy_assigment + elsif lastDigitCreatedAt < 4 # 2,3 + :random_assigment + else # 4,5,6,7,8,9 + :recommended_assignment + end + end + +end \ No newline at end of file