class ExerciseQuestionsController < ApplicationController before_action :require_login, :check_auth #用户需登陆 before_action :get_exercise, only: [:new, :create] #获取试卷 before_action :get_exercise_question, except: [:new, :create] #获取试卷的问题及试卷 before_action :is_course_teacher #是否为老师 before_action :validate_params, only: [:create, :update] #传入参数的验证 before_action :check_exercise_status, only: [:new, :create, :delete_answer, :destroy] #除未发布状态之外,其余状态不能进行增删操作 before_action :cannot_change_column, only: [:update] #更新时不能更改的内容 before_action :check_before_adjust_score, only: [:adjust_score] before_action :check_before_batch_adjust_score, only: [:batch_adjust_score] before_action :check_before_common_adjust_score, only: [:adjust_score, :batch_adjust_score] before_action :check_exercise_users, only: [:batch_adjust_score] include ExercisesHelper def new ActiveRecord::Base.transaction do begin @exercise_question = @exercise.exercise_questions.new rescue Exception => e uid_logger_error(e.message) tip_exception("页面访问失败失败!") raise ActiveRecord::Rollback end end end #question_type 0为单选题,1为多选题,2为判断题,3为填空题(单空和多空),4为简答题,5为实训题 def create ActiveRecord::Base.transaction do question_options = { :question_title => params[:question_title], :question_type => params[:question_type].present? ? params[:question_type].to_i : 0, #默认为单选题 :question_number => @exercise.exercise_questions.count + 1, :question_score => params[:question_score].present? ? params[:question_score].to_f.round(1) : 5.0, :shixun_id => params[:shixun_id].blank? ? "" : params[:shixun_id], :is_ordered => params[:is_ordered] # 填空题的答案是否为一一对应关系,默认为true即为一一对应 } @exercise_question = @exercise.exercise_questions.new(question_options) #插入问题时,那么从插入的这个id以后的question_num都将要+1 if params[:insert_id].present? insert_exercise = @exercise.exercise_questions.find_by(id: params[:insert_id]) if insert_exercise.present? #如果该问题存在的话,意思是如果是第一题,那么就不存在插入 ques_num = insert_exercise.question_number.to_i @exercise_question.question_number = ques_num + 1 #更新了问题的位置 @exercise.exercise_questions.insert_question_ex(ques_num).update_all("question_number = question_number + 1") end end if @exercise_question.save! #为选择题(包括单选和多选)的时候,创建问题选项 ques_type = @exercise_question.question_type if ques_type <= Exercise::MULTIPLE choices_array = params[:question_choices] choices_count = choices_array.count (1..choices_count).each do |c| choice = choices_array[c - 1] #每一个选项的内容 choice_option = { :choice_position => c, :choice_text => choice } question_choices = @exercise_question.exercise_choices.new(choice_option) question_choices.save! end standard_answer = params[:standard_answers] #为数组格式,因为可能会有单选和多选,标准答案,已提前判断不能为空, standard_answer = standard_answer.uniq.reject(&:blank?) #标准答案的存储,如:["1","2","3"..]等,1对应A,2对应B,3对应C。。。 standard_answer.each do |a| choice_id = a.to_i standard_option = { :exercise_question_id => @exercise_question.id, :exercise_choice_id => choice_id #即为选择的位置参数 } question_standard_answer = ExerciseStandardAnswer.new(standard_option) question_standard_answer.save! if standard_answer.count > 1 && ques_type == Exercise::SINGLE #当标准答案数大于1,且不为多选时,修改为多选 @exercise_question.update_attribute("question_type", Exercise::MULTIPLE) elsif standard_answer.count == 1 && ques_type == Exercise::MULTIPLE @exercise_question.update_attribute("question_type", Exercise::SINGLE) end end elsif ques_type == Exercise::JUDGMENT #这个为判断题 choices_array = params[:question_choices] #判断的选项,对/错等等 choices_count = choices_array.count (1..choices_count).each do |c| choice = choices_array[c - 1] #每一个选项的内容 choice_option = { :choice_position => c, :choice_text => choice } question_choices = @exercise_question.exercise_choices.create!(choice_option) question_choices.save! end standard_answer = params[:standard_answers] #对应选项的id standard_option = { :exercise_question_id => @exercise_question.id, :exercise_choice_id => standard_answer.first.to_i } question_standard_answer = ExerciseStandardAnswer.new(standard_option) question_standard_answer.save! elsif ques_type == Exercise::COMPLETION #填空题,每空的参考答案有多个,那么以位置对应 standard_answer = params[:standard_answers] standard_answer.each do |a| null_choice_id = a[:choice_id] null_choice_text = a[:answer_text] null_choice_text.each do |n| standard_option = { :exercise_question_id => @exercise_question.id, :exercise_choice_id => null_choice_id, :answer_text => n } question_standard_answer = ExerciseStandardAnswer.new(standard_option) question_standard_answer.save! end end elsif ques_type == Exercise::SUBJECTIVE #简答题 if params[:standard_answers].present? && params[:standard_answers].reject(&:blank?).count > 0 standard_answer = params[:standard_answers] standard_answer.each do |a| standard_option = { :exercise_question_id => @exercise_question.id, :answer_text => a, } question_standard_answer = ExerciseStandardAnswer.new(standard_option) question_standard_answer.save! end end elsif ques_type == Exercise::PRACTICAL #实训题 shixun = Shixun.find_by(id: params[:shixun_id]) shixun_scores = params[:question_scores] #试卷有多个的分值有多个分数表,所以为分数的数组 shixun_name = params[:shixun_name] || shixun.name question_score = 0 shixun.challenges.try(:each_with_index) do |challenge, index| shixun_option = { :challenge_id => challenge.id, :shixun_id => shixun.id, :exercise_question_id => @exercise_question.id, :position => (index + 1), :question_score => shixun_scores[index].present? ? shixun_scores[index].to_f.round(1) : 5 } ex_shixun_challenge = ExerciseShixunChallenge.create!(shixun_option) question_score += ex_shixun_challenge.question_score # 问题的分数,为各个关卡分数的总和 end @exercise_question.update!(:question_score => question_score, :shixun_name => shixun_name) end end end end def show ActiveRecord::Base.transaction do begin @exercise_choices = @exercise_question.exercise_choices @exercise_question_shixun = @exercise_question.exercise_shixun_challenges rescue Exception => e uid_logger_error(e.message) tip_exception("页面调用失败!") raise ActiveRecord::Rollback end end end def edit ActiveRecord::Base.transaction do @exercise_choices = @exercise_question.exercise_choices @exercise_question_shixun = @exercise_question.exercise_shixun_challenges end end def update ActiveRecord::Base.transaction do standard_answer_change = false # 更新试卷题目的内容 question_options = { :question_title => params[:question_title], :is_ordered => params[:is_ordered], # 填空题的答案是否为一一对应关系,默认为true即为一一对应 :question_score => params[:question_score].present? ? params[:question_score].to_f.round(1) : 5.0 #不可修改分数 } choices_array = params[:question_choices] stan_answer_params = params[:standard_answers] standard_answer = stan_answer_params.present? ? stan_answer_params.uniq.reject(&:blank?) : [] @exercise_question.update!(question_options) #当选项存在时,可修改选项内容,但是不能更改选项的位置(即不能增删选项) if choices_array.present? ex_choices = @exercise_question.exercise_choices ex_choice_count = ex_choices.count choice_array_count = choices_array.count ex_choice_count_array = (1..ex_choice_count).to_a choice_array_count_array = (1..choice_array_count).to_a if ex_choice_count > choice_array_count #如果选项有减少的,那么只更新传入的,删除以前的 choice_array_count_array.each do |c| choice = choices_array[c - 1] #每一个选项的内容 exercise_choice = @exercise_question.exercise_choices.find_choice_custom("choice_position", (c)) exercise_choice.update(choice_text: choice) end drop_ex_choice = @exercise_question.exercise_choices.left_choice_choose("choice_position", (choice_array_count)) drop_ex_choice.destroy_all else ex_choice_count_array.each do |c| choice = choices_array[c - 1] #每一个选项的内容 exercise_choice = @exercise_question.exercise_choices.find_choice_custom("choice_position", (c)) exercise_choice.update(choice_text: choice) end new_add_choice = choice_array_count_array - ex_choice_count_array #新传入的需新增 if new_add_choice.count > 0 new_add_choice.each do |i| choice_option = { :choice_position => i, :choice_text => choices_array[i - 1] } question_choices = @exercise_question.exercise_choices.new(choice_option) question_choices.save! end end end end #试卷未发布时,当标准答案存在时,可修改标准答案内容,可增删标准答案,否则只能修改标准答案,不能增删标准答案 @exercise_answers_array = @exercise_question.exercise_standard_answers #问卷的全部标准答案 if standard_answer.present? if @exercise_question.question_type <= Exercise::JUDGMENT #选择题/判断题,标准答案为一个或多个 exercise_standard_choices = @exercise_answers_array.pluck(:exercise_choice_id) #问题以前的全部标准答案选项位置 if exercise_standard_choices.sort != standard_answer.sort #表示答案有更改的 standard_answer_change = true common_standard_choices = standard_answer & exercise_standard_choices # 传入的标准答案的选项位置和以前的并集,即表示不用做更改的 old_left_standard_choices = exercise_standard_choices - common_standard_choices # 以前的差集共同的,剩余的表示需要删掉 new_left_standard_choices = standard_answer - common_standard_choices # 传入的标准答案差集共同的,剩余的表示需要新建 if old_left_standard_choices.count > 0 @exercise_answers_array.standard_by_ids(old_left_standard_choices).destroy_all end if new_left_standard_choices.count > 0 #新建标准答案 new_left_standard_choices.each do |s| standard_option = { :exercise_question_id => @exercise_question.id, :exercise_choice_id => s.to_i #即为选择的位置参数 } question_standard_answer = ExerciseStandardAnswer.new(standard_option) question_standard_answer.save! end end if standard_answer.count > 1 && @exercise_question.question_type == Exercise::SINGLE #当标准答案数大于1,且不为多选时,修改为多选 @exercise_question.update_attribute("question_type", Exercise::MULTIPLE) elsif standard_answer.count == 1 && @exercise_question.question_type == Exercise::MULTIPLE @exercise_question.update_attribute("question_type", Exercise::SINGLE) end end elsif @exercise_question.question_type == Exercise::COMPLETION #填空题 old_ex_answer = @exercise_question.exercise_standard_answers #当前问题的全部标准答案 old_ex_answer_choice_texts = old_ex_answer.pluck(:answer_text).sort new_ex_answer_choice_texts = standard_answer.pluck(:answer_text).sum.sort if old_ex_answer_choice_texts != new_ex_answer_choice_texts #填空题标准答案有更改时,才会更新标准答案 new_ex_answer_choice_ids = standard_answer.map {|a| a[:choice_id]}.uniq #新传入的答案数组序号 old_ex_answer_choice_ids = old_ex_answer.pluck(:exercise_choice_id).uniq #全部的答案数组序号 standard_answer_change = true #删除多余的选项 if old_ex_answer_choice_ids.count > new_ex_answer_choice_ids.count #有减少的填空 delete_ex_answer_choice_ids = old_ex_answer_choice_ids - new_ex_answer_choice_ids old_ex_answer.standard_by_ids(delete_ex_answer_choice_ids).destroy_all end standard_answer.each do |aa| null_choice_id = aa[:choice_id] null_choice_text = aa[:answer_text] null_choice_text_count = null_choice_text.count #当前传入的答案数量 null_choice_text_count_array = (1..null_choice_text_count).to_a ex_answer_pre = old_ex_answer.standard_by_ids(null_choice_id) #当前问题的全部答案 ex_answer_pre_count = ex_answer_pre.count ex_answer_pre_count_array = (1..ex_answer_pre_count).to_a if old_ex_answer_choice_ids.include?(null_choice_id) #以前的填空题答案包含有现在的填空序号 if null_choice_text_count >= ex_answer_pre_count new_add_choice = null_choice_text_count_array - ex_answer_pre_count_array ex_answer_pre_count_array.each do |n| @hash_symbol_null_ = { :exercise_question_id => @exercise_question.id, :exercise_choice_id => null_choice_id, :answer_text => null_choice_text[n - 1] } standard_option = @hash_symbol_null_ ex_answer_pre[n - 1].update(standard_option) end if new_add_choice.count > 0 #表示有新增的 new_add_choice.each do |i| standard_option = { :exercise_question_id => @exercise_question.id, :exercise_choice_id => null_choice_id, :answer_text => null_choice_text[i - 1] } question_standard_answer = ExerciseStandardAnswer.new(standard_option) question_standard_answer.save! end end else new_delete_choice = ex_answer_pre_count_array - null_choice_text_count_array null_choice_text.each_with_index do |n, index| standard_option = { :exercise_question_id => @exercise_question.id, :exercise_choice_id => null_choice_id, :answer_text => n } ex_answer_pre[index].update(standard_option) end if new_delete_choice.count > 0 #表示填空题的答案有删减的 new_delete_choice.each do |d| ex_answer_pre[d - 1].destroy end end end else null_choice_text.each do |n| standard_option = { :exercise_question_id => @exercise_question.id, :exercise_choice_id => null_choice_id, :answer_text => n } question_standard_answer = ExerciseStandardAnswer.new(standard_option) question_standard_answer.save! end end end end end end if @exercise_question.question_type == Exercise::SUBJECTIVE #主观题 main_standard_answer = standard_answer.present? ? standard_answer.first : nil if @exercise_answers_array.present? @exercise_answers_array.first.update_attribute("answer_text", main_standard_answer) else standard_option = { :exercise_question_id => @exercise_question.id, :answer_text => main_standard_answer, } question_standard_answer = ExerciseStandardAnswer.new(standard_option) question_standard_answer.save! end elsif @exercise_question.question_type == Exercise::PRACTICAL question_score = 0 shixun_name = params[:shixun_name] || @exercise_question.shixun_name @exercise_question.exercise_shixun_challenges.each_with_index do |challenge, index| challenge.question_score = params[:question_scores][index].to_f.round(1) challenge.save! question_score += params[:question_scores][index].to_f.round(1) end @exercise_question.question_score = question_score @exercise_question.shixun_name = shixun_name @exercise_question.save! elsif @exercise_question.question_type == Exercise::PROGRAM if @exercise_question.hack.present? @exercise_question.hack.update!(name: params[:question_title], description: params[:description]) end end #当试卷已发布时(试卷的总状态),当标准答案修改时,如有已提交的学生,需重新计算分数. if standard_answer_change && @exercise.exercise_status >= Exercise::PUBLISHED # ex_users_committed = @exercise.exercise_users.exercise_user_committed # if ex_users_committed.size > 0 # ex_users_committed.each do |ex_user| # update_objective_score = update_single_score(@exercise_question,ex_user.user_id,standard_answer) # if update_objective_score != 0 # objective_score = ex_user.objective_score # new_objective_score = objective_score + update_objective_score # total_score = ex_user.score + update_objective_score # total_score = total_score < 0.0 ? 0.0 : total_score # ex_user.update_attributes(objective_score:new_objective_score,score:total_score) # end # end # end normal_status(3, "修改了标准答案\n是否重新计算学生答题的成绩?") else normal_status(0, "试卷更新成功!") end end end def update_scores ActiveRecord::Base.transaction do begin standard_answer = params[:standard_answers] ex_users_committed = @exercise.exercise_users.exercise_user_committed if ex_users_committed.size > 0 ex_users_committed.each do |ex_user| update_objective_score = update_single_score(@exercise_question, ex_user.user_id, standard_answer) if update_objective_score != 0 objective_score = ex_user.objective_score new_objective_score = objective_score + update_objective_score total_score = ex_user.score + update_objective_score total_score = total_score < 0.0 ? 0.0 : total_score ex_user.update!(objective_score: new_objective_score, score: total_score) end end end normal_status(0, "学生成绩更新成功") rescue Exception => e uid_logger_error(e.message) tip_exception("答案删除失败!") end end end # 试卷问题的上下移动 def up_down ActiveRecord::Base.transaction do begin opr = params[:opr] current_q_p = @exercise_question.question_number.to_i #问题的当前位置 if @exercise.exercise_status.to_i == Exercise::UNPUBLISHED if opr.present? if opr.to_s == "up" last_q_p = @exercise.exercise_questions.find_by(question_number: (current_q_p - 1)) # 当前问题的前一个问题 if last_q_p.present? @exercise_question.update_attribute('question_number', (current_q_p - 1)) last_q_p.update_attribute('question_number', current_q_p) # 重新获取当前问题的位置 normal_status(0, "问题上移成功!") else normal_status(-1, "移动失败,已经是第一个问题了!") end elsif opr.to_s == "down" next_q_p = @exercise.exercise_questions.find_by(question_number: (current_q_p + 1)) # 当前问题的前一个问题 if next_q_p.present? @exercise_question.update_attribute('question_number', (current_q_p + 1)) next_q_p.update_attribute('question_number', current_q_p) normal_status(0, "问题下移成功!") else normal_status(-1, "移动失败,已经是最后一个问题了!") end end else normal_status(-1, "移动失败,请输入参数") end else normal_status(-1, "已发布的不能移动问题") end rescue Exception => e uid_logger_error(e.message) tip_exception("问题移动失败!") end end end #试卷选项的删除 def delete_answer ActiveRecord::Base.transaction do begin choice_d_id = params[:choice_no].to_i # 选项的当前位置 question_choices = @exercise_question.exercise_choices delete_answer = question_choices.find_by(choice_position: choice_d_id) left_choices = question_choices.where("choice_position > ? ", choice_d_id) left_choices.update_all("choice_position = choice_position - 1") if left_choices if delete_answer.destroy normal_status(0, "答案删除成功!") else normal_status(-1, "答案删除失败!") end rescue Exception => e uid_logger_error(e.message) tip_exception("答案删除失败!") end end end #试卷问题的删除 def destroy ActiveRecord::Base.transaction do begin question_d_id = @exercise_question.question_number.to_i #问题的当前位置 exercise_questions = @exercise.exercise_questions left_questions = exercise_questions.where("question_number > ?", question_d_id) left_questions.update_all("question_number = question_number - 1") if left_questions if @exercise_question.destroy normal_status(0, "问题删除成功!") else normal_status(-1, "问题删除失败!") end rescue Exception => e uid_logger_error(e.message) tip_exception("问题删除失败!") end end end #老师调分窗口 def adjust_score ActiveRecord::Base.transaction do ex_all_scores = @exercise.exercise_questions.pluck(:question_score).sum ex_obj_score = @exercise_current_user.objective_score #全部客观题得分 ex_subj_score = @exercise_current_user.subjective_score < 0.0 ? 0.0 : @exercise_current_user.subjective_score #全部主观题得分 ex_answers = @exercise_question.exercise_answers.where(user_id: @user_id) #当前用户对该问题的回答 # 当前试卷用户所有的主观题的回答 all_subjective_exercise_answers = @exercise.exercise_answers.where(user_id: @user_id).joins(:exercise_question).where(exercise_questions: {question_type: Exercise::SUBJECTIVE}) # 当前试卷用户的所有主观题 if @exercise.is_random? all_subjective_questions = @exercise_current_user.exercise_questions.subjectives else all_subjective_questions = @exercise.exercise_questions.subjectives end if @exercise_question.question_type == Exercise::MULTIPLE if ex_answers.present? #学生有回答时 取学生的答题得分,否则0分 answer_choice_array = [] ex_answers.each do |a| if a.try(:exercise_choice).try(:choice_position).present? answer_choice_array.push(a&.exercise_choice&.choice_position) #学生答案的位置 end end user_answer_content = answer_choice_array.reject(&:blank?).sort standard_answer = @exercise_question.exercise_standard_answers.pluck(:exercise_choice_id).sort if standard_answer.size == 1 # 老数据需要判断学生答题是否正确, 正确取原题得分,否则是0分 standard_answer = standard_answer.first.to_s.split("").map(&:to_i).sort if user_answer_content == standard_answer ex_answer_old = @exercise_question.question_score else ex_answer_old = 0 end else # 新多选题只需取第一条答题记录的得分 ex_answer_old = ex_answers.first.score > 0 ? ex_answers.first.score : 0 end ex_answers.update_all(:score => @c_score) #所有的正确选项需重新更新 else answer_option = { :user_id => @user_id, :exercise_question_id => @exercise_question.id, :score => @c_score, :answer_text => "" } ExerciseAnswer.create!(answer_option) # exercise_answer = ExerciseAnswer.find_or_initialize_by(answer_option.slice(:user_id, :exercise_question_id)) # exercise_answer.assign_attributes(answer_option.slice(:score, :answer_text)) # exercise_answer.save! ex_answer_old = 0 end if ex_obj_score <= 0.0 new_obj_score = @c_score else new_obj_score = ex_obj_score - ex_answer_old + @c_score end total_scores = new_obj_score + ex_subj_score if total_scores < 0.0 total_scores = 0.0 elsif total_scores > ex_all_scores total_scores = ex_all_scores end ex_scores = { :objective_score => new_obj_score, :score => total_scores } @exercise_current_user.update!(ex_scores) elsif @exercise_question.question_type == Exercise::COMPLETION #当为填空题,更新问题的总分, if ex_answers.exists? ex_answer_old = ex_answers.score_reviewed.pluck(:score).sum #每一关的得分总和 each_right_score = (@c_score / ex_answers.count.to_f) #调分后,平均每关的分数 new_obj_score = ex_obj_score - ex_answer_old + @c_score ex_answers.update_all(:score => each_right_score) #所有的正确选项需重新更新 else #如果学生未答,则创建新的答题记录 answer_option = { :user_id => @user_id, :exercise_question_id => @exercise_question.id, :score => @c_score, :answer_text => "" } ExerciseAnswer.create!(answer_option) new_obj_score = ex_obj_score + @c_score end total_scores = new_obj_score + ex_subj_score if total_scores < 0.0 total_scores = 0.0 elsif total_scores > ex_all_scores total_scores = ex_all_scores end ex_scores = { :objective_score => new_obj_score, :score => total_scores } @exercise_current_user.update!(ex_scores) elsif @exercise_question.question_type == Exercise::SUBJECTIVE #当为主观题时 if ex_answers.exists? ex_answers_old_score = ex_answers.first.score > 0.0 ? ex_answers.first.score : 0.0 #原分数小于0,取0 new_sub_score = ex_subj_score - ex_answers_old_score + @c_score #原全部主观题总分减去原该主观题得分再加调分后的分数,即为当前全部主观题得分 ex_answers.first.update_attribute("score", @c_score) else #如果学生未答,则创建新的答题记录 answer_option = { :user_id => @user_id, :exercise_question_id => @exercise_question.id, :score => @c_score, :answer_text => "" } ExerciseAnswer.create!(answer_option) new_sub_score = ex_subj_score + @c_score end total_scores = ex_obj_score + new_sub_score if total_scores < 0.0 total_scores = 0.0 elsif total_scores > ex_all_scores total_scores = ex_all_scores end # 判断当前学生的试卷中是否所有的主观题都已评阅,如果所有主观题都评阅过才将reviewed置为true(主观题对应的回答的分数大于等于0即为已评阅,因为默认为-1) if (all_subjective_exercise_answers.reload.size == all_subjective_questions.size) && all_subjective_exercise_answers.all?{|ex| ex.score>=0} reviewed = true else reviewed = false end ex_scores = { :subjective_score => new_sub_score, :score => total_scores, :subjective_reviewed => 1, :reviewed => reviewed } @exercise_current_user.update!(ex_scores) elsif @exercise_question.question_type == Exercise::PRACTICAL ex_answers = @exercise_question.exercise_shixun_answers.where(user_id: @user_id, exercise_shixun_challenge_id: @shixun_a_id) if ex_answers.present? #当为实训题时 ex_shixun_old_score = ex_answers.first.score > 0.0 ? ex_answers.first.score : 0.0 new_obj_score = ex_obj_score - ex_shixun_old_score + @c_score ex_answers.first.update_attribute("score", @c_score) else ex_shixun_option = { :exercise_question_id => @exercise_question.id, :user_id => @user_id, :exercise_shixun_challenge_id => @shixun_a_id, :score => @c_score, :status => 0 } ExerciseShixunAnswer.create!(ex_shixun_option) new_obj_score = ex_obj_score + @c_score end total_scores = new_obj_score + ex_subj_score if total_scores < 0.0 total_scores = 0.0 elsif total_scores > ex_all_scores total_scores = ex_all_scores end ex_scores = { :objective_score => new_obj_score, :score => total_scores } @exercise_current_user.update!(ex_scores) elsif @exercise_question.question_type == Exercise::PROGRAM if ex_answers.exists? ex_pro_old_score = ex_answers.first.score > 0.0 ? ex_answers.first.score : 0.0 new_obj_score = ex_obj_score - ex_pro_old_score + @c_score ex_answers.first.update_attribute("score", @c_score) else #如果学生未答,则创建新的答题记录 answer_option = { :user_id => @user_id, :exercise_question_id => @exercise_question.id, :score => @c_score, :answer_text => "" } ExerciseAnswer.create!(answer_option) new_obj_score = ex_obj_score + @c_score end total_scores = new_obj_score + ex_subj_score if total_scores < 0.0 total_scores = 0.0 elsif total_scores > ex_all_scores total_scores = ex_all_scores end ex_scores = { :objective_score => new_obj_score, :score => total_scores } @exercise_current_user.update!(ex_scores) end comments = params[:comment] if @exercise_question.question_type == Exercise::PRACTICAL shixun_answer = ExerciseShixunAnswer.find_by(exercise_question_id: @exercise_question.id, user_id: @user_id, exercise_shixun_challenge_id: @shixun_a_id) answer_comment = shixun_answer&.exercise_answer_comments&.take else question_answer = ExerciseAnswer.find_by(exercise_question_id: @exercise_question.id, user_id: @user_id) answer_comment = question_answer&.exercise_answer_comments&.take end if answer_comment.present? answer_comment.update!(:comment => comments, :score => @c_score) else ExerciseAnswerComment.create!(:comment => comments, :score => @c_score, :user_id => current_user.id, :exercise_question_id => @exercise_question.id, :exercise_shixun_answer_id => shixun_answer&.id, :exercise_answer_id => question_answer&.id) unless Tiding.where(user_id: @user_id, trigger_user_id: current_user.id, parent_container_id: @exercise.id, parent_container_type: "ExerciseScore").exists? Tiding.create!(user_id: @user_id, trigger_user_id: current_user.id, container_id: @exercise.id, container_type: "Exercise", parent_container_id: @exercise.id, parent_container_type: "ExerciseScore", belong_container_id: @course.id, belong_container_type: 'Course', tiding_type: "Exercise") end end # question_comment = @exercise_question.exercise_answer_comments&.first # # if question_comment.present? # comment_option = { # :comment => comments, # :score => @c_score, # :exercise_answer_id => ex_answers.present? ? ex_answers.first.id : nil, # :user_id => current_user.id # } # question_comment.update!(comment_option) # @exercise_comments = question_comment # else # ex_answer_comment_id = @exercise_question.exercise_answers.find_by(user_id: @user_id).try(:id) # comment_option = { # :user_id => current_user.id, # :comment => comments, # :score => @c_score, # :exercise_question_id => @exercise_question.id, # :exercise_shixun_answer_id => @shixun_a_id.present? ? @shixun_a_id : nil, # :exercise_answer_id => ex_answer_comment_id # } # @exercise_comments = ExerciseAnswerComment.new(comment_option) # @exercise_comments.save! # # # 给被评阅人发送消息,同一个教师评阅无需重复发消息 # # unless Tiding.where(user_id: @user_id, trigger_user_id: current_user.id, parent_container_id: @exercise.id, parent_container_type: "ExerciseScore").exists? # Tiding.create!(user_id: @user_id, trigger_user_id: current_user.id, container_id: @exercise.id, # container_type: "Exercise", parent_container_id: @exercise.id, # parent_container_type: "ExerciseScore", belong_container_id: @course.id, # belong_container_type: 'Course', tiding_type: "Exercise") # end # # end end end def batch_adjust_score_copy ActiveRecord::Base.transaction do # 以user_id为key,用户对应的exericse_answers数组为value的hash, 替代 ex_answers exercise_answers_hash = @exercise_question.exercise_answers. includes(:exercise_answer_comments). select(:score, :user_id).group_by{|ex_answer| ex_answer.user_id} exercise_shixun_answers_hash = @exercise_question.exercise_shixun_answers. includes(:exercise_answer_comments). where(exercise_shixun_challenge_id: @shixun_a_id). select(:score, :user_id).group_by{|ex_answer| ex_answer.user_id} exercise_question_id = @exercise_question.id current_user_id = current_user.id # 以user_id为key,用户对应的主观题的exericse_answers数组为value的hash, 替代 all_subjective_exercise_answers all_subjective_exercise_answers_hash = @exercise.subjective_exercise_answers.select(:score, :user_id).group_by{|ex_answer| ex_answer.user_id} # 用于填空题,有得分的回答集合 替代 ex_answers.score_reviewed ex_answers_score_reviewed_hash = @exercise_question.exercise_answers.score_reviewed.select(:score, :user_id).group_by{|ex_answer| ex_answer.user_id} # 该试卷下所有主观题 all_subjective_exercise_questions = @exercise.exercise_questions.subjectives @exercise_users.each do |exercise_user| ex_all_scores = @exercise.exercise_questions.pluck(:question_score).sum ex_obj_score = exercise_user.objective_score #全部客观题得分 ex_subj_score = exercise_user.subjective_score < 0.0 ? 0.0 : exercise_user.subjective_score #全部主观题得分 # ex_answers = @exercise_question.exercise_answers.where(user_id: exercise_user.user_id) #当前用户答案的得分 # 该用户所有的主观题的回答 # all_subjective_exercise_answers = @exercise.subjective_exercise_answers.where(user_id: exercise_user.user_id) if @exercise_question.question_type == Exercise::SUBJECTIVE #当为主观题时 if exercise_answers_hash[exercise_user.user_id].present? ex_answers_old_score = exercise_answers_hash[exercise_user.user_id].first.score > 0.0 ? exercise_answers_hash[exercise_user.user_id].first.score : 0.0 #原分数小于0,取0 new_sub_score = ex_subj_score - ex_answers_old_score + @c_score #原全部主观题总分减去原该主观题得分再加调分后的分数,即为当前全部主观题得分 exercise_answers_hash[exercise_user.user_id].first.update_attribute("score", @c_score) else #如果学生未答,则创建新的答题记录 answer_option = { :user_id => exercise_user.user_id, :exercise_question_id => exercise_question_id, :score => @c_score, :answer_text => "" } ExerciseAnswer.create!(answer_option) new_sub_score = ex_subj_score + @c_score end total_scores = ex_obj_score + new_sub_score if total_scores < 0.0 total_scores = 0.0 elsif total_scores > ex_all_scores total_scores = ex_all_scores end # 判断当前学生的试卷中是否所有的主观题都已评阅,如果所有主观题都评阅过才将reviewed置为true(主观题对应的回答的分数大于等于0即为已评阅,因为默认为-1) if (all_subjective_exercise_answers_hash[exercise_user.user_id]&.size == all_subjective_exercise_questions.size) && all_subjective_exercise_answers_hash[exercise_user.user_id]&.all?{|ex| ex.score>=0} reviewed = true else reviewed = false end ex_scores = { :subjective_score => new_sub_score, :score => total_scores, :subjective_reviewed => 1, :reviewed => reviewed } exercise_user.update!(ex_scores) end # if @exercise_question.question_type == Exercise::MULTIPLE # if exercise_answers_hash[exercise_user.user_id].present? #学生有回答时 取学生的答题得分,否则0分 # answer_choice_array = [] # exercise_answers_hash[exercise_user.user_id].each do |a| # if a.try(:exercise_choice).try(:choice_position).present? # answer_choice_array.push(a&.exercise_choice&.choice_position) #学生答案的位置 # end # end # user_answer_content = answer_choice_array.reject(&:blank?).sort # standard_answer = @exercise_question.exercise_standard_answers.pluck(:exercise_choice_id).sort # if standard_answer.size == 1 # 老数据需要判断学生答题是否正确, 正确取原题得分,否则是0分 # standard_answer = standard_answer.first.to_s.split("").map(&:to_i).sort # if user_answer_content == standard_answer # ex_answer_old = @exercise_question.question_score # else # ex_answer_old = 0 # end # else # 新多选题只需取第一条答题记录的得分 # ex_answer_old = exercise_answers_hash[exercise_user.user_id].first.score > 0 ? exercise_answers_hash[exercise_user.user_id].first.score : 0 # end # exercise_answers_hash[exercise_user.user_id].update_all(:score => @c_score) #所有的正确选项需重新更新 # else # answer_option = { # :user_id => exercise_user.user_id, # :exercise_question_id => exercise_question_id, # :score => @c_score, # :answer_text => "" # } # ExerciseAnswer.create!(answer_option) # # exercise_answer = ExerciseAnswer.find_or_initialize_by(answer_option.slice(:user_id, :exercise_question_id)) # # exercise_answer.assign_attributes(answer_option.slice(:score, :answer_text)) # # exercise_answer.save! # ex_answer_old = 0 # end # if ex_obj_score <= 0.0 # new_obj_score = @c_score # else # new_obj_score = ex_obj_score - ex_answer_old + @c_score # end # # total_scores = new_obj_score + ex_subj_score # if total_scores < 0.0 # total_scores = 0.0 # elsif total_scores > ex_all_scores # total_scores = ex_all_scores # end # ex_scores = { # :objective_score => new_obj_score, # :score => total_scores # } # @exercise_current_user.update!(ex_scores) # # elsif @exercise_question.question_type == Exercise::COMPLETION #当为填空题,更新问题的总分, # # if exercise_answers_hash[exercise_user.user_id].exists? # ex_answer_old = ex_answers_score_reviewed_hash[exercise_user.user_id].pluck(:score).sum #每一关的得分总和 # each_right_score = (@c_score / exercise_answers_hash[exercise_user.user_id].count.to_f) #调分后,平均每关的分数 # new_obj_score = ex_obj_score - ex_answer_old + @c_score # exercise_answers_hash[exercise_user.user_id].update_all(:score => each_right_score) #所有的正确选项需重新更新 # else #如果学生未答,则创建新的答题记录 # answer_option = { # :user_id => exercise_user.user_id, # :exercise_question_id => exercise_question_id, # :score => @c_score, # :answer_text => "" # } # ExerciseAnswer.create!(answer_option) # new_obj_score = ex_obj_score + @c_score # end # # total_scores = new_obj_score + ex_subj_score # if total_scores < 0.0 # total_scores = 0.0 # elsif total_scores > ex_all_scores # total_scores = ex_all_scores # end # ex_scores = { # :objective_score => new_obj_score, # :score => total_scores # } # @exercise_current_user.update!(ex_scores) # elsif @exercise_question.question_type == Exercise::SUBJECTIVE #当为主观题时 # if exercise_answers_hash[exercise_user.user_id].present? # ex_answers_old_score = exercise_answers_hash[exercise_user.user_id].first.score > 0.0 ? exercise_answers_hash[exercise_user.user_id].first.score : 0.0 #原分数小于0,取0 # new_sub_score = ex_subj_score - ex_answers_old_score + @c_score #原全部主观题总分减去原该主观题得分再加调分后的分数,即为当前全部主观题得分 # exercise_answers_hash[exercise_user.user_id].first.update_attribute("score", @c_score) # else #如果学生未答,则创建新的答题记录 # answer_option = { # :user_id => exercise_user.user_id, # :exercise_question_id => exercise_question_id, # :score => @c_score, # :answer_text => "" # } # ExerciseAnswer.create!(answer_option) # new_sub_score = ex_subj_score + @c_score # end # total_scores = ex_obj_score + new_sub_score # if total_scores < 0.0 # total_scores = 0.0 # elsif total_scores > ex_all_scores # total_scores = ex_all_scores # end # # 判断当前学生的试卷中是否所有的主观题都已评阅,如果所有主观题都评阅过才将reviewed置为true(主观题对应的回答的分数大于等于0即为已评阅,因为默认为-1) # if (all_subjective_exercise_answers_hash[exercise_user.user_id]&.size == all_subjective_exercise_questions.size) && all_subjective_exercise_answers_hash[exercise_user.user_id]&.all?{|ex| ex.score>=0} # reviewed = true # else # reviewed = false # end # ex_scores = { # :subjective_score => new_sub_score, # :score => total_scores, # :subjective_reviewed => 1, # :reviewed => reviewed # } # exercise_user.update!(ex_scores) # # elsif @exercise_question.question_type == Exercise::PRACTICAL # ex_answers = @exercise_question.exercise_shixun_answers.where(user_id: exercise_user.user_id, exercise_shixun_challenge_id: @shixun_a_id) # # if ex_answers.present? #当为实训题时 # ex_shixun_old_score = ex_answers.first.score > 0.0 ? ex_answers.first.score : 0.0 # new_obj_score = ex_obj_score - ex_shixun_old_score + @c_score # ex_answers.first.update_attribute("score", @c_score) # else # ex_shixun_option = { # :exercise_question_id => exercise_question_id, # :user_id => exercise_user.user_id, # :exercise_shixun_challenge_id => @shixun_a_id, # :score => @c_score, # :status => 0 # } # ExerciseShixunAnswer.create!(ex_shixun_option) # new_obj_score = ex_obj_score + @c_score # end # total_scores = new_obj_score + ex_subj_score # if total_scores < 0.0 # total_scores = 0.0 # elsif total_scores > ex_all_scores # total_scores = ex_all_scores # end # ex_scores = { # :objective_score => new_obj_score, # :score => total_scores # } # exercise_user.update!(ex_scores) # elsif @exercise_question.question_type == Exercise::PROGRAM # if exercise_answers_hash[exercise_user.user_id].exists? # ex_pro_old_score = exercise_answers_hash[exercise_user.user_id].first.score > 0.0 ? exercise_answers_hash[exercise_user.user_id].first.score : 0.0 # new_obj_score = ex_obj_score - ex_pro_old_score + @c_score # exercise_answers_hash[exercise_user.user_id].first.update_attribute("score", @c_score) # else #如果学生未答,则创建新的答题记录 # answer_option = { # :user_id => exercise_user.user_id, # :exercise_question_id => exercise_question_id, # :score => @c_score, # :answer_text => "" # } # ExerciseAnswer.create!(answer_option) # new_obj_score = ex_obj_score + @c_score # end # # total_scores = new_obj_score + ex_subj_score # if total_scores < 0.0 # total_scores = 0.0 # elsif total_scores > ex_all_scores # total_scores = ex_all_scores # end # ex_scores = { # :objective_score => new_obj_score, # :score => total_scores # } # exercise_user.update!(ex_scores) # end comments = params[:comment] if @exercise_question.question_type == Exercise::PRACTICAL shixun_answer = exercise_shixun_answers_hash[exercise_user.user_id]&.first answer_comment = shixun_answer&.exercise_answer_comments&.take else question_answer = exercise_answers_hash[exercise_user.user_id]&.first answer_comment = question_answer&.exercise_answer_comments&.take end if answer_comment.present? answer_comment.update!(:comment => comments, :score => @c_score) else ExerciseAnswerComment.create!(:comment => comments, :score => @c_score, :user_id => current_user_id, :exercise_question_id => exercise_question_id, :exercise_shixun_answer_id => shixun_answer&.id, :exercise_answer_id => question_answer&.id) unless Tiding.where(user_id: exercise_user.user_id, trigger_user_id: current_user_id, parent_container_id: @exercise.id, parent_container_type: "ExerciseScore").exists? Tiding.create!(user_id: exercise_user.user_id, trigger_user_id: current_user_id, container_id: @exercise.id, container_type: "Exercise", parent_container_id: @exercise.id, parent_container_type: "ExerciseScore", belong_container_id: @course.id, belong_container_type: 'Course', tiding_type: "Exercise") end end end normal_status("操作成功") end end def batch_adjust_score # 覆盖全部, 为true的话,如果学生试卷之前有过评阅则更新其评阅记录,否则不更新 @cover_all = params[:cover_all] ActiveRecord::Base.transaction do # 以user_id为key,用户对应的exericse_answers数组为value的hash, 替代 ex_answers exercise_answers_hash = @exercise_question.exercise_answers. includes(:exercise_answer_comments).group_by{|ex_answer| ex_answer.user_id} exercise_shixun_answers_hash = @exercise_question.exercise_shixun_answers. includes(:exercise_answer_comments). where(exercise_shixun_challenge_id: @shixun_a_id).group_by{|ex_answer| ex_answer.user_id} exercise_question_id = @exercise_question.id current_user_id = current_user.id # 以user_id为key,用户对应的主观题的exericse_answers数组为value的hash, 替代 all_subjective_exercise_answers all_subjective_exercise_answers_hash = @exercise.subjective_exercise_answers.select(:id, :score, :user_id).group_by{|ex_answer| ex_answer.user_id} # 用于填空题,有得分的回答集合 替代 ex_answers.score_reviewed ex_answers_score_reviewed_hash = @exercise_question.exercise_answers.score_reviewed.select(:score, :user_id).group_by{|ex_answer| ex_answer.user_id} # 该试卷下所有主观题的数量 all_subjective_exercise_questions_size = @exercise.exercise_questions.subjectives.size # 预先查出所有tiding,避免在循环中再次通过查询验证是否存在 tiding_user_id_array = Tiding.where( user_id: @exercise_users.pluck(:user_id), trigger_user_id: current_user_id, parent_container_id: @exercise.id, parent_container_type: "ExerciseScore" ).pluck(:user_id) # 试卷总分 ex_all_scores = @exercise.exercise_questions.pluck(:question_score).sum # 通知的属性数组,用于循环结束后批量创建tiding tiding_attrs = [] # 试卷评论数组,用于循环结束后批量创建评论 exercise_answer_comment_attrs = [] # 试卷回答数组,用于循环结束后批量创建试卷回答 exercise_answer_attrs = [] # 批量更新试卷用户的sql exercise_user_update_sql = "UPDATE exercise_users SET " fields = %W(subjective_score score subjective_reviewed reviewed) fields_values = {} fields.each { |d| fields_values[d] = []} exercise_user_ids = [] @exercise_users.each do |exercise_user| exercise_answers = exercise_answers_hash[exercise_user.user_id] exercise_shixun_answers = exercise_shixun_answers_hash[exercise_user.user_id] # 如果之前有过评阅且勾选了覆盖之前评阅,则更新或者创建分数 ex_obj_score = exercise_user.objective_score #全部客观题得分 ex_subj_score = exercise_user.subjective_score < 0.0 ? 0.0 : exercise_user.subjective_score #全部主观题得分 # ex_answers = @exercise_question.exercise_answers.where(user_id: exercise_user.user_id) #当前用户答案的得分 # 该用户所有的主观题的回答 # all_subjective_exercise_answers = @exercise.subjective_exercise_answers.where(user_id: exercise_user.user_id) # 批量评阅现在只对主观题进行评阅 if @exercise_question.question_type == Exercise::SUBJECTIVE #当为主观题时 @min_score = @min_score.to_i @max_score = @max_score.to_i @score = (@min_score..@max_score).to_a.sample if exercise_answers_hash[exercise_user.user_id].present? # 该用户的回答 exercise_answer = exercise_answers_hash[exercise_user.user_id].first # 如果之前有过评阅而且禁止覆盖之前的评阅,那么将跳过该学生的评阅 next if (exercise_answer.score > 0.0) && !@cover_all ex_answers_old_score = exercise_answer.score > 0.0 ? exercise_answer.score : 0.0 #原分数小于0,取0 new_sub_score = ex_subj_score - ex_answers_old_score + @score #原全部主观题总分减去原该主观题得分再加调分后的分数,即为当前全部主观题得分 exercise_answer.update_attribute("score", @score) else #如果学生未答,则创建新的答题记录 exercise_answer_attrs << { :user_id => exercise_user.user_id, :exercise_question_id => exercise_question_id, :score => @score, :answer_text => "" } new_sub_score = ex_subj_score + @score end total_scores = ex_obj_score + new_sub_score if total_scores < 0.0 total_scores = 0.0 elsif total_scores > ex_all_scores total_scores = ex_all_scores end # 判断当前学生的试卷中是否所有的主观题都已评阅,如果所有主观题都评阅过才将reviewed置为true(主观题对应的回答的分数大于等于0即为已评阅,因为默认为-1), 或者 整个试卷只有一道主观题,那么也可以直接将状态置为已评阅 if ((all_subjective_exercise_answers_hash[exercise_user.user_id].try(:reject){|ea| ea.id == exercise_answer&.id}&.size == (all_subjective_exercise_questions_size-1)) && all_subjective_exercise_answers_hash[exercise_user.user_id].try(:reject){|ea| ea.id == exercise_answer&.id}.try(:all?){|ex| ex.score>=0}) || (all_subjective_exercise_questions_size==1) reviewed = 1 else reviewed = 0 end exercise_user_ids << exercise_user.id ex_scores = { :subjective_score => new_sub_score, :score => total_scores, :subjective_reviewed => 1, :reviewed => reviewed } fields.each do |d| fields_values[d] << " WHEN #{exercise_user.id} THEN #{ex_scores[d.to_sym] || 'null'} " end end comments = params[:comment] question_answer = exercise_answers_hash[exercise_user.user_id]&.first answer_comment = question_answer&.exercise_answer_comments&.take if answer_comment.present? answer_comment.update!(:comment => comments, :score => @c_score) else exercise_answer_comment_attrs << { :comment => comments, :score => @c_score, :user_id => current_user_id, :exercise_question_id => exercise_question_id, :exercise_answer_id => question_answer&.id, :exercise_user_id => exercise_user.id } unless tiding_user_id_array.include? exercise_user.user_id tiding_attrs << { user_id: exercise_user.user_id, trigger_user_id: current_user_id, container_id: @exercise.id, container_type: "Exercise", parent_container_id: @exercise.id, parent_container_type: "ExerciseScore", belong_container_id: @course.id, belong_container_type: 'Course', tiding_type: "Exercise" } end end # if @exercise_question.question_type == Exercise::MULTIPLE # if exercise_answers_hash[exercise_user.user_id].present? #学生有回答时 取学生的答题得分,否则0分 # answer_choice_array = [] # exercise_answers_hash[exercise_user.user_id].each do |a| # if a.try(:exercise_choice).try(:choice_position).present? # answer_choice_array.push(a&.exercise_choice&.choice_position) #学生答案的位置 # end # end # user_answer_content = answer_choice_array.reject(&:blank?).sort # standard_answer = @exercise_question.exercise_standard_answers.pluck(:exercise_choice_id).sort # if standard_answer.size == 1 # 老数据需要判断学生答题是否正确, 正确取原题得分,否则是0分 # standard_answer = standard_answer.first.to_s.split("").map(&:to_i).sort # if user_answer_content == standard_answer # ex_answer_old = @exercise_question.question_score # else # ex_answer_old = 0 # end # else # 新多选题只需取第一条答题记录的得分 # ex_answer_old = exercise_answers_hash[exercise_user.user_id].first.score > 0 ? exercise_answers_hash[exercise_user.user_id].first.score : 0 # end # exercise_answers_hash[exercise_user.user_id].update_all(:score => @c_score) #所有的正确选项需重新更新 # else # answer_option = { # :user_id => exercise_user.user_id, # :exercise_question_id => exercise_question_id, # :score => @c_score, # :answer_text => "" # } # ExerciseAnswer.create!(answer_option) # # exercise_answer = ExerciseAnswer.find_or_initialize_by(answer_option.slice(:user_id, :exercise_question_id)) # # exercise_answer.assign_attributes(answer_option.slice(:score, :answer_text)) # # exercise_answer.save! # ex_answer_old = 0 # end # if ex_obj_score <= 0.0 # new_obj_score = @c_score # else # new_obj_score = ex_obj_score - ex_answer_old + @c_score # end # # total_scores = new_obj_score + ex_subj_score # if total_scores < 0.0 # total_scores = 0.0 # elsif total_scores > ex_all_scores # total_scores = ex_all_scores # end # ex_scores = { # :objective_score => new_obj_score, # :score => total_scores # } # @exercise_current_user.update!(ex_scores) # elsif @exercise_question.question_type == Exercise::COMPLETION #当为填空题,更新问题的总分, # # if exercise_answers_hash[exercise_user.user_id].exists? # ex_answer_old = ex_answers_score_reviewed_hash[exercise_user.user_id].pluck(:score).sum #每一关的得分总和 # each_right_score = (@c_score / exercise_answers_hash[exercise_user.user_id].count.to_f) #调分后,平均每关的分数 # new_obj_score = ex_obj_score - ex_answer_old + @c_score # exercise_answers_hash[exercise_user.user_id].update_all(:score => each_right_score) #所有的正确选项需重新更新 # else #如果学生未答,则创建新的答题记录 # answer_option = { # :user_id => exercise_user.user_id, # :exercise_question_id => exercise_question_id, # :score => @c_score, # :answer_text => "" # } # ExerciseAnswer.create!(answer_option) # new_obj_score = ex_obj_score + @c_score # end # # total_scores = new_obj_score + ex_subj_score # if total_scores < 0.0 # total_scores = 0.0 # elsif total_scores > ex_all_scores # total_scores = ex_all_scores # end # ex_scores = { # :objective_score => new_obj_score, # :score => total_scores # } # @exercise_current_user.update!(ex_scores) # elsif @exercise_question.question_type == Exercise::SUBJECTIVE #当为主观题时 # @min_score = @min_score.to_i # @max_score = @max_score.to_i # @score = (@min_score..@max_score).to_a.sample # if exercise_answers_hash[exercise_user.user_id].present? # ex_answers_old_score = exercise_answers_hash[exercise_user.user_id].first.score > 0.0 ? exercise_answers_hash[exercise_user.user_id].first.score : 0.0 #原分数小于0,取0 # new_sub_score = ex_subj_score - ex_answers_old_score + @c_score #原全部主观题总分减去原该主观题得分再加调分后的分数,即为当前全部主观题得分 # exercise_answers_hash[exercise_user.user_id].first.update_attribute("score", @c_score) # else #如果学生未答,则创建新的答题记录 # answer_option = { # :user_id => exercise_user.user_id, # :exercise_question_id => exercise_question_id, # :score => @score, # :answer_text => "" # } # ExerciseAnswer.create!(answer_option) # new_sub_score = ex_subj_score + @score # end # total_scores = ex_obj_score + new_sub_score # if total_scores < 0.0 # total_scores = 0.0 # elsif total_scores > ex_all_scores # total_scores = ex_all_scores # end # # 判断当前学生的试卷中是否所有的主观题都已评阅,如果所有主观题都评阅过才将reviewed置为true(主观题对应的回答的分数大于等于0即为已评阅,因为默认为-1) # if (all_subjective_exercise_answers_hash[exercise_user.user_id]&.size == all_subjective_exercise_questions_size) && all_subjective_exercise_answers_hash[exercise_user.user_id]&.all?{|ex| ex.score>=0} # reviewed = true # else # reviewed = false # end # ex_scores = { # :subjective_score => new_sub_score, # :score => total_scores, # :subjective_reviewed => 1, # :reviewed => reviewed # } # exercise_user.update!(ex_scores) # elsif @exercise_question.question_type == Exercise::PRACTICAL # ex_answers = @exercise_question.exercise_shixun_answers.where(user_id: exercise_user.user_id, exercise_shixun_challenge_id: @shixun_a_id) # # if ex_answers.present? #当为实训题时 # ex_shixun_old_score = ex_answers.first.score > 0.0 ? ex_answers.first.score : 0.0 # new_obj_score = ex_obj_score - ex_shixun_old_score + @c_score # ex_answers.first.update_attribute("score", @c_score) # else # ex_shixun_option = { # :exercise_question_id => exercise_question_id, # :user_id => exercise_user.user_id, # :exercise_shixun_challenge_id => @shixun_a_id, # :score => @c_score, # :status => 0 # } # ExerciseShixunAnswer.create!(ex_shixun_option) # new_obj_score = ex_obj_score + @c_score # end # total_scores = new_obj_score + ex_subj_score # if total_scores < 0.0 # total_scores = 0.0 # elsif total_scores > ex_all_scores # total_scores = ex_all_scores # end # ex_scores = { # :objective_score => new_obj_score, # :score => total_scores # } # exercise_user.update!(ex_scores) # elsif @exercise_question.question_type == Exercise::PROGRAM # if exercise_answers_hash[exercise_user.user_id].exists? # ex_pro_old_score = exercise_answers_hash[exercise_user.user_id].first.score > 0.0 ? exercise_answers_hash[exercise_user.user_id].first.score : 0.0 # new_obj_score = ex_obj_score - ex_pro_old_score + @c_score # exercise_answers_hash[exercise_user.user_id].first.update_attribute("score", @c_score) # else #如果学生未答,则创建新的答题记录 # answer_option = { # :user_id => exercise_user.user_id, # :exercise_question_id => exercise_question_id, # :score => @c_score, # :answer_text => "" # } # ExerciseAnswer.create!(answer_option) # new_obj_score = ex_obj_score + @c_score # end # # total_scores = new_obj_score + ex_subj_score # if total_scores < 0.0 # total_scores = 0.0 # elsif total_scores > ex_all_scores # total_scores = ex_all_scores # end # ex_scores = { # :objective_score => new_obj_score, # :score => total_scores # } # exercise_user.update!(ex_scores) # end end fields.each do |d| exercise_user_update_sql += "#{d} = CASE id " + fields_values[d].join(" ") + " END" + (d == 'reviewed' ? "" : ", ") end exercise_user_update_sql += " WHERE id IN (#{exercise_user_ids.join(',')})" ActiveRecord::Base.connection.execute(exercise_user_update_sql) Tiding.bulk_insert( values: tiding_attrs ) ExerciseAnswerComment.bulk_insert( values: exercise_answer_comment_attrs ) ExerciseAnswer.bulk_insert( values: exercise_answer_attrs ) # 由于在评阅过程中exercise_answer可能是新建的,而现在保存exercise_answer放在最后统一执行,所以循环中的exercise_answer有可能不存在 # 所以需要在前面更新完成之后再次检查exercise_answer_comments中exercise_answer_id为空的记录进行更新 # 需要拼装一个 exercise_user_id,exercise_question_id => exercise_answer_id exercise_answers = @exercise_question.exercise_answers.where(user_id: @exercise_users.pluck(:user_id)).group_by{|ea| ea.user_id} grouped_exercise_users = @exercise_users.group_by(&:user_id) update_exercise_answer_comment_sql = "update exercise_answer_comments set exercise_answer_id = case " exercise_answers.each do |user_id, sub_exercise_answers| update_exercise_answer_comment_sql += " when exercise_user_id = #{grouped_exercise_users[user_id].first.id} and exercise_question_id = #{@exercise_question.id} then #{sub_exercise_answers[0].id} " end update_exercise_answer_comment_sql += " end where exercise_question_id = #{@exercise_question.id} and exercise_answer_id is null" ActiveRecord::Base.connection.execute(update_exercise_answer_comment_sql) normal_status("操作成功") end end def update_question_score tip_exception(-1, "分值不允许为空!") if params[:question_score].blank? && params[:question_scores].blank? #分值的数组或参数必需存在一个 @exercise_question.update!(question_score: params[:question_score]) normal_status("更新成功") end private def questions_params params.require(:exercise_question).permit(:question_title, :question_type, :question_number, :exercise_id, :question_score, :shixun_id, :is_ordered) end def get_exercise @exercise = Exercise.find_by(id: params[:exercise_id]) if @exercise.blank? tip_exception(404, "试卷不存在") else @course = @exercise.course if @course.blank? tip_exception(404, "课堂不存在") end end end def is_course_teacher @identity = current_user.course_identity(@course) unless @identity < Course::STUDENT #为老师/助教/管理员 tip_exception("403", "无权限操作") end end def get_exercise_question @exercise_question = ExerciseQuestion.find_by(id: params[:id]) if @exercise_question.present? @exercise = @exercise_question.exercise if @exercise.blank? tip_exception(404, "试卷不存在") else @course = @exercise.course if @course.blank? tip_exception(404, "课堂不存在") end end else tip_exception(404, "试卷问题不存在") end end def validate_params normal_status(-1, "题目不允许为空!") if (params[:question_title].blank? && params[:question_type].to_i != Exercise::PRACTICAL) #除了实训题,其余题目必需有题干 normal_status(-1, "问题类型不允许为空!") if params[:question_type].blank? normal_status(-1, "分值不允许为空!") if params[:question_score].blank? && params[:question_scores].blank? #分值的数组或参数必需存在一个 if params[:question_score].present? && params[:question_score].to_f <= 0.0 #问题类型存在,则分值不能为空,且必需大于0 normal_status(-1, "分值必需大于0!") elsif (params[:question_score].present? && params[:question_score].to_f.round(1) > 100.0) || (params[:question_scores].present? && (params[:question_scores].map {|a| a.to_f.round(1)}.max > 100.0)) normal_status(-1, "分值不能超过100分!") elsif params[:question_scores].present? && params[:question_scores].include?(0.0) #如果有负数,则自动取绝对值,#多个分数值,针对实训题的 normal_status(-1, "分值必需大于0!") elsif params[:standard_answers].present? && params[:question_choices].present? && (params[:standard_answers].count > params[:question_choices].count) normal_status(-1, "标准答案数不能大于选项数!") elsif [0, 1, 2, 3].include?(params[:question_type].to_i) && (params[:standard_answers].blank? || params[:standard_answers].include?("")) #选择题/判断题/填空题 问题选项/标准答案不能为空,也不能包含空的内容 normal_status(-1, "标准答案不能为空!") elsif params[:question_type].to_i == 2 && (params[:standard_answers].count > 1 || params[:question_choices].blank? || params[:question_choices].include?("")) #判断题的标准答案不能大于1个,选项不能为空 normal_status(-1, "判断题选项不能为空/标准答案不能大于1个!") elsif params[:question_type].to_i <= 1 && (params[:question_choices].blank? || params[:question_choices].include?("") || params[:question_choices].count < 2) #选择题选项不能为空,且不能小于2 normal_status(-1, "选择题选项内容不能为空,且不能少于2个!") elsif params[:question_type].to_i == 3 && (params[:standard_answers].blank? || params[:standard_answers].count > 5) #填空题选项最多为5个,且如果为1个的话,不允许修改is_ordered normal_status(-1, "填空题标准答案不能为空/不能超过5个!") elsif params[:question_type].to_i == 4 && params[:standard_answers].count > 2 #简单题参考答案最多为1个 normal_status(-1, "简答题的参考答案不能大于2个!") elsif params[:question_type].to_i == 5 if params[:shixun_id].blank? #实训题的id不能为空 normal_status(-1, "实训题id不能为空!") elsif params[:shixun_name].blank? normal_status(-1, "实训题名称不能为空!") end elsif params[:question_type].to_i == 6 normal_status(-1, "编程题描述不能为空!") if params[:description].blank? end end def check_exercise_status normal_status(-1, "不能更改试卷问题!") if @exercise.exercise_status != Exercise::UNPUBLISHED end #更新时不能修改的内容 def cannot_change_column #已发布的/已截止的/评阅中的状态时,不能修改分数,不能增删问题和答案,不能修改标准答案,可以修改选项内容/题目内容,这里仅指单个问题 if @exercise.exercise_status != Exercise::UNPUBLISHED question_score = @exercise_question.question_score #原来的分数 update_question_score = params[:question_score].to_f.round(1) #传入的分数 choices_count = @exercise_question.exercise_choices.size #原来的选项个数 exercise_choice_ids = @exercise_question.exercise_standard_answers.pluck(:exercise_choice_id).uniq standard_answers_text = @exercise_question.exercise_standard_answers.pluck(:answer_text).uniq update_choices_count = params[:question_choices].present? ? params[:question_choices].count : choices_count #传入的选项个数 standard_answer = params[:standard_answers] #传参数是怎么传的?能不能传空值? if update_question_score.present? && question_score != update_question_score #分数有更改 normal_status(-1, "已发布/已截止,分数不允许修改!") elsif update_choices_count != choices_count #选项个数有修改 normal_status(-1, "已发布/已截止,不允许增删答案!") elsif standard_answer.present? if @exercise_question.question_type == Exercise::COMPLETION # exercise_answers_text = standard_answer.map{|a| a[:answer_text]}.sum.uniq # unless (standard_answer.count == exercise_choice_ids.count) && (standard_answers_text.count == exercise_answers_text.count) unless standard_answer.count == exercise_choice_ids.count normal_status(-1, "已发布/已截止,不允许增删标准答案!") end elsif @exercise_question.question_type == Exercise::SUBJECTIVE unless standard_answers_text.count == standard_answer.count normal_status(-1, "已发布/已截止,不允许增删标准答案!") end end end end end # 不论是批量评阅还是单独评阅都需要校验的逻辑 def check_before_common_adjust_score @c_score = params[:score].to_f #调分后的分数 if @exercise_question.question_type == Exercise::SINGLE || @exercise_question.question_type == Exercise::JUDGMENT normal_status(-1, "单选题/判断题不能调分!") elsif params[:comment].present? && params[:comment].length > 100 normal_status(-1, "评语不能超过100个字符!") else @shixun_a_id = params[:shixun_challenge_id] if @exercise_question.question_type == Exercise::PRACTICAL #当为实训题时,为关卡的分数 @shixun_challenge = @exercise_question.exercise_shixun_challenges.cha_id_find(@shixun_a_id) if @shixun_challenge.present? @old_ques_score = @shixun_challenge.first.question_score else @old_ques_score = nil normal_status(-1, "实训答案id不能为空!") end else @old_ques_score = @exercise_question.question_score #当不为实训题时,为各问题的分数 end if @old_ques_score.present? && (@c_score > @old_ques_score) normal_status(-1, "分数不能大于题目分数!") end end end def check_before_adjust_score @c_score = params[:score].to_f #调分后的分数 @user_id = params[:user_id] @exercise_current_user = @exercise.exercise_users.exercise_commit_users(@user_id).first #当前试卷用户的答案内容 if @exercise_current_user.blank? normal_status(-1, "用户不存在!") elsif @c_score.blank? normal_status(-1, "分数不能为空!") end end def check_before_batch_adjust_score tip_exception(-1, "随机试卷暂不支持批量评阅") if @exercise.is_random? @exercise_users = @exercise.exercise_users.where(user_id: params[:user_ids]) if params[:user_ids].present? #批量评阅时,前端传过来的是id数组 @min_score = params[:min_score] @max_score = params[:max_score] if @exercise_users.blank? normal_status(-1, "用户不存在!") elsif @min_score.blank? || @max_score.blank? normal_status(-1, "分数不能为空!") end end def check_exercise_users tip_exception(-1, "批量评阅仅支持对评阅中、未评阅的学生进行") if @exercise_users.exists?(reviewed: true) tip_exception(-1, "不支持对未交卷/考试中的学生进行评阅") if @exercise_users.exists?(commit_status: "none_commmit") end end