Merge pull request #100 from ThommyH/seperate_user_in_ABC

Seperate user in groups for experiment in OOP2017
This commit is contained in:
rteusner
2017-03-24 15:42:13 +01:00
committed by GitHub
5 changed files with 131 additions and 85 deletions

View File

@ -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();

View File

@ -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

View File

@ -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

View File

@ -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'

View File

@ -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