Distinguish between intermediate and final feedback

* Also add more information to user_exercise_feedback
* Migrate existing feedback to enrich with submissions
This commit is contained in:
Sebastian Serth
2020-10-20 13:50:04 +02:00
parent 712810dada
commit ad467fa58f
7 changed files with 63 additions and 9 deletions

View File

@ -617,7 +617,7 @@ class ExercisesController < ApplicationController
end end
else else
# redirect to feedback page if score is less than 100 percent # redirect to feedback page if score is less than 100 percent
if @exercise.needs_more_feedback? && !@embed_options[:disable_redirect_to_feedback] if @exercise.needs_more_feedback?(@submission) && !@embed_options[:disable_redirect_to_feedback]
clear_lti_session_data(@submission.exercise_id, @submission.user_id, session[:consumer_id]) clear_lti_session_data(@submission.exercise_id, @submission.user_id, session[:consumer_id])
redirect_to_user_feedback redirect_to_user_feedback
else else

View File

@ -97,7 +97,26 @@ class UserExerciseFeedbacksController < ApplicationController
end end
def uef_params def uef_params
params[:user_exercise_feedback].permit(:feedback_text, :difficulty, :exercise_id, :user_estimated_worktime).merge(user_id: current_user.id, user_type: current_user.class.name) if params[:user_exercise_feedback].present? return unless params[:user_exercise_feedback].present?
exercise_id = if params[:user_exercise_feedback].nil?
params[:exercise_id]
else
params[:user_exercise_feedback][:exercise_id]
end
user_id = current_user.id
user_type = current_user.class.name
latest_submission = Submission
.where(user_id: user_id, user_type: user_type, exercise_id: exercise_id)
.order(created_at: :desc).first
params[:user_exercise_feedback]
.permit(:feedback_text, :difficulty, :exercise_id, :user_estimated_worktime)
.merge(user_id: user_id,
user_type: user_type,
submission: latest_submission,
normalized_score: latest_submission.normalized_score)
end end
def validate_inputs(uef_params) def validate_inputs(uef_params)

View File

@ -46,7 +46,7 @@ class Exercise < ApplicationRecord
@working_time_statistics = nil @working_time_statistics = nil
attr_reader :working_time_statistics attr_reader :working_time_statistics
MAX_EXERCISE_FEEDBACKS = 20 MAX_GROUP_EXERCISE_FEEDBACKS = 20
def average_percentage def average_percentage
if average_score && (maximum_score != 0.0) && submissions.exists?(cause: 'submit') if average_score && (maximum_score != 0.0) && submissions.exists?(cause: 'submit')
@ -550,8 +550,12 @@ class Exercise < ApplicationRecord
end end
private :valid_submission_deadlines? private :valid_submission_deadlines?
def needs_more_feedback? def needs_more_feedback?(submission)
user_exercise_feedbacks.size <= MAX_EXERCISE_FEEDBACKS if submission.normalized_score == 1.00
user_exercise_feedbacks.final.size <= MAX_GROUP_EXERCISE_FEEDBACKS
else
user_exercise_feedbacks.intermediate.size <= MAX_GROUP_EXERCISE_FEEDBACKS
end
end end
def last_submission_per_user def last_submission_per_user

View File

@ -101,9 +101,9 @@ class Submission < ApplicationRecord
end end
def redirect_to_feedback? def redirect_to_feedback?
# Redirect 10% of users to the exercise feedback page. Ensure, that always the # Redirect 10% of users to the exercise feedback page. Ensure, that always the same
# same users get redirected per exercise and different users for different exercises. # users get redirected per exercise and different users for different exercises. If
# If desired, the number of feedbacks can be limited with exercise.needs_more_feedback? # desired, the number of feedbacks can be limited with exercise.needs_more_feedback?(submission)
(user_id + exercise.created_at.to_i) % 10 == 1 (user_id + exercise.created_at.to_i) % 10 == 1
end end

View File

@ -2,10 +2,14 @@ class UserExerciseFeedback < ApplicationRecord
include Creation include Creation
belongs_to :exercise belongs_to :exercise
belongs_to :submission, optional: true
has_one :execution_environment, through: :exercise has_one :execution_environment, through: :exercise
validates :user_id, uniqueness: { scope: [:exercise_id, :user_type] } validates :user_id, uniqueness: { scope: [:exercise_id, :user_type] }
scope :intermediate, -> { where.not(normalized_score: 1.00) }
scope :final, -> { where(normalized_score: 1.00) }
def to_s def to_s
"User Exercise Feedback" "User Exercise Feedback"
end end

View File

@ -0,0 +1,23 @@
class AddNormalizedScoreAndSubmissionToUserExerciseFeedback < ActiveRecord::Migration[5.2]
def change
add_column :user_exercise_feedbacks, :normalized_score, :float
add_reference :user_exercise_feedbacks, :submission, foreign_key: true
# Disable automatic timestamp modification
ActiveRecord::Base.record_timestamps = false
UserExerciseFeedback.all.find_each do |uef|
latest_submission = Submission
.where(user_id: uef.user_id, user_type: uef.user_type, exercise_id: uef.exercise_id)
.where('created_at < ?', uef.updated_at)
.order(created_at: :desc).first
# In the beginning, CodeOcean allowed feedback for exercises while viewing an RfC. As a RfC
# might be opened by any registered learner, feedback for exercises was created by learners
# without having any submission for this particular exercise.
next if latest_submission.nil?
uef.update(submission: latest_submission, normalized_score: latest_submission.normalized_score)
end
ActiveRecord::Base.record_timestamps = true
end
end

View File

@ -10,7 +10,7 @@
# #
# It's strongly recommended that you check this file into your version control system. # It's strongly recommended that you check this file into your version control system.
ActiveRecord::Schema.define(version: 2020_10_07_104221) do ActiveRecord::Schema.define(version: 2020_10_19_090123) do
# These are extensions that must be enabled in order to support this database # These are extensions that must be enabled in order to support this database
enable_extension "plpgsql" enable_extension "plpgsql"
@ -423,6 +423,9 @@ ActiveRecord::Schema.define(version: 2020_10_07_104221) do
t.integer "user_estimated_worktime" t.integer "user_estimated_worktime"
t.datetime "created_at", null: false t.datetime "created_at", null: false
t.datetime "updated_at", null: false t.datetime "updated_at", null: false
t.float "normalized_score"
t.bigint "submission_id"
t.index ["submission_id"], name: "index_user_exercise_feedbacks_on_submission_id"
end end
create_table "user_exercise_interventions", id: :serial, force: :cascade do |t| create_table "user_exercise_interventions", id: :serial, force: :cascade do |t|
@ -455,4 +458,5 @@ ActiveRecord::Schema.define(version: 2020_10_07_104221) do
add_foreign_key "request_for_comments", "submissions", name: "request_for_comments_submissions_id_fk" add_foreign_key "request_for_comments", "submissions", name: "request_for_comments_submissions_id_fk"
add_foreign_key "submissions", "study_groups" add_foreign_key "submissions", "study_groups"
add_foreign_key "tips", "file_types" add_foreign_key "tips", "file_types"
add_foreign_key "user_exercise_feedbacks", "submissions"
end end