module ExercisesHelper # include GitHelper #获取每个学生对每个题的答案状态 def get_each_student_exercise(exercise_id,exercise_questions,user_id) @exercise_user = ExerciseUser.current_exercise_user(user_id,exercise_id).first exercise_obj_status = exercise_questions.find_objective_questions @ex_obj_array = [] exercise_obj_status.each do |q| if q.question_type == 5 ques_score = q.exercise_shixun_answers.search_shixun_answers("user_id",user_id).pluck(:score).sum else ques_score = q.exercise_answers.search_answer_users("user_id",user_id).score_reviewed.pluck(:score).sum end if ques_score >= q.question_score #满分作答为正确 ques_score = q.question_score stand_answer = 1 elsif ques_score > 0.0 #部分作答 stand_answer = 2 else stand_answer = 0 end ques_option = { "q_id":q.id, #该问题的id "q_type":q.question_type, "q_position":q.question_number, #该问题的位置 "stand_status":stand_answer, #用户是否回答 "user_score":ques_score.round(1).to_s #每个问题的总得分 } @ex_obj_array.push(ques_option) end exercise_sub_status = exercise_questions.find_by_custom("question_type",4) #主观题 @ex_sub_array = [] #主观题的已答/未答 exercise_sub_status.each do |s| sub_answer = s.exercise_answers.search_answer_users("user_id",user_id) #主观题只有一个回答 if sub_answer.present? && sub_answer.first.score >= 0.0 if s.question_score <= sub_answer.first.score stand_status = 1 else stand_status = 2 end # stand_status = 1 sub_answer_score = sub_answer.first.score else stand_status = 0 sub_answer_score = nil end sub_score = { "q_id":s.id, "q_type":s.question_type, "q_position":s.question_number, "stand_status":stand_status, "user_score":sub_answer_score.present? ? sub_answer_score.round(1).to_s : nil } @ex_sub_array.push(sub_score) end @ex_obj_array = @ex_obj_array.sort_by {|k| k[:q_position]} @ex_sub_array = @ex_sub_array.sort_by {|k| k[:q_position]} end #试卷的统计结果页面计算各题的 def exercise_commit_result(questions,user_ids) question_infos = [] questions.each do |ex| ex_total_score = user_ids.count * ex&.question_score #该试卷的已回答的总分 ex_answers = ex.exercise_answers if ex.question_type != 5 ques_title = ex.question_title ques_less_title = nil effictive_users = ex_answers.search_answer_users("user_id",user_ids) else ques_title = ex.shixun.name ques_less_title = ex.question_title effictive_users = ex.exercise_shixun_answers.search_shixun_answers("user_id",user_ids) end effictive_users_count = effictive_users.count #有效回答数,可能有重复的用户id,这里仅统计是否回答这个问题的全部人数 ex_answered_scores = effictive_users.score_reviewed.pluck(:score).sum #该问题的全部得分 if ex_total_score == 0.0 percent = 0.0 else percent = (ex_answered_scores / ex_total_score.to_f).round(3) * 100 #正确率 end question_answer_infos = [] if ex.question_type <= 2 #单选题 standard_answer = ex.exercise_standard_answers.pluck(:exercise_choice_id) #标准答案的位置 ex.exercise_choices.each do |c| if standard_answer.include?(c.choice_position) #选项的标准答案为选项的位置 right_answer = true else right_answer = false end answer_this_choice = effictive_users.search_exercise_answer("exercise_choice_id",c.id) answer_users_count = answer_this_choice.count if effictive_users_count == 0 answer_percent = 0.0 else answer_percent = (answer_users_count / effictive_users_count.to_f ).round(3) end answer_option = { :choice_position => c.choice_position, :choice_text => c.choice_text, :choice_users_count => answer_users_count, :choice_percent => answer_percent.round(1), :right_answer => right_answer } question_answer_infos.push(answer_option) end elsif ex.question_type == 3 #填空题 ex_ordered = ex.is_ordered null_standard_answer = ex.exercise_standard_answers null_stand_choice = null_standard_answer.pluck(:exercise_choice_id) null_stand_text = null_standard_answer.pluck(:answer_text) standard_answer_count = 0 all_user_count = 0 null_stand_choice.each_with_index do |s,index| user_count = 0 s_choice_text = null_stand_text[index] if ex_ordered #有序排列 user_ids.each do |u| user_answers = ex_answers.search_answer_users("user_id",u).search_answer_users("exercise_choice_id",s) user_answers_choice = user_answers.present? ? user_answers.first.answer_text : "" if s_choice_text == user_answers_choice user_count += 1 end end else user_count = user_count + effictive_users.search_exercise_answer("answer_text",s_choice_text).count #回答了标准答案的用户 end if effictive_users_count == 0 answer_percent = 0.0 else answer_percent = (user_count / effictive_users_count.to_f ).round(3) end answer_option = { :choice_position => index+1, :choice_text => s_choice_text, :choice_users_count => user_count, :choice_percent => answer_percent.round(1), :right_answer => true } question_answer_infos.push(answer_option) all_user_count += user_count standard_answer_count += 1 end user_wrong_count = (effictive_users_count - all_user_count ) if effictive_users_count > 0 && user_wrong_count >= 0 wrong_percent = (user_wrong_count / effictive_users_count.to_f ).round(3) else wrong_percent = 0.0 end wrong_answer_position = { :choice_position => (standard_answer_count + 1), :choice_text => "wrong", :choice_users_count => user_wrong_count, :choice_percent => wrong_percent.round(1), :right_answer => false } question_answer_infos.push(wrong_answer_position) elsif ex.question_type == 4 #主观题 ex_score = ex&.question_score full_scores = effictive_users.search_exercise_answer("score",ex_score).count #满分人数 no_full_scores = effictive_users.exercise_no_full_scores(ex_score).count #部分分数人数 zero_scores = effictive_users.search_exercise_answer("score",0.0).count #包含为0分的,及未评阅的 # review_scores = ex.exercise_answer_comments.count #主观题的评阅数量 un_review_scores = effictive_users_count - full_scores - no_full_scores - zero_scores #未评阅数 if un_review_scores < 0 un_review_scores = 0 end # zero_scores = all_zero_scores - un_review_scores #已评阅,且答案未0分的人数 main_scores_array = [full_scores,no_full_scores,zero_scores,un_review_scores] main_scores_array.each_with_index do |s,index| if index == 0 right_answer = true else right_answer = false end if effictive_users_count == 0 || s < 0 s = 0 score_percent = 0.0 else score_percent = (s.to_i / effictive_users_count.to_f ).round(3) end answer_option = { :choice_position => index+1, :choice_text => index+1, :choice_users_count => s, :choice_percent => score_percent.round(1), :right_answer => right_answer } question_answer_infos.push(answer_option) end elsif ex.question_type == 5 #实训题 ex.exercise_shixun_challenges.each do |c| cha_score = c&.question_score cha_shixun_answer = effictive_users.search_shixun_keys("exercise_shixun_challenge_id",c.id) effictive_users_count = cha_shixun_answer.count #实训题的每个关卡的有效填写量 full_scores = cha_shixun_answer.search_shixun_keys("score",cha_score).count #满分人数 no_full_scores = cha_shixun_answer.shixun_no_full_scores(cha_score).count #部分分数人数c all_zero_scores = cha_shixun_answer.search_shixun_keys("score",0.0).count #满分人数 shixun_score_array = [full_scores,no_full_scores,all_zero_scores] shixun_chas = [] shixun_score_array.each_with_index do |s,index| if index == 0 right_answer = true else right_answer = false end if effictive_users_count == 0 score_percent = 0.0 else score_percent = (s.to_i / effictive_users_count.to_f ).round(3) end answer_option = { :choice_position => index+1, :choice_text => index+1, :choice_users_count => s, :choice_percent => score_percent.round(1), :right_answer => right_answer } shixun_chas.push(answer_option) end shixun_new_chas = { :cha_id => c.challenge_id, :cha_name => c.challenge.subject, :cha_position => c.position, :cha_details => shixun_chas } question_answer_infos.push(shixun_new_chas) end end ques_option = { :ques_title => ques_title, :ques_less_title => ques_less_title, #副标题,仅实训题才有 :type => ex.question_type, :position => ex.question_number, :percent => percent.round(1).to_s, :ques_effictive_counts => effictive_users_count, :ques_details => question_answer_infos } question_infos.push(ques_option) end question_infos end #获取试卷的已答/未答人数 def get_exercise_answers(ex_users) @commit_ex_users = ex_users.commit_exercise_by_status(1) #当前老师的全部学生中已提交的 @exercise_answers = @commit_ex_users.present? ? @commit_ex_users.size : 0 #表示已经提交了的用户 course_all_members_count = ex_users.present? ? ex_users.size : 0 @exercise_unanswers = (course_all_members_count - @exercise_answers) end def exercise_index_show(exercise,course,is_teacher_or,user) # exercise_all_users = exercise.exercise_users ex_show_text = [] if course.is_end #课堂停止后,试卷显示为已结束 exercise_status = 4 elsif is_teacher_or == 1 #当前为老师的时候,显示的是老师身份的对应试卷的状态,因为该试卷,可能对应老师的多个分班 exercise_status = exercise.exercise_status else exercise_status = exercise.get_exercise_status(user.id) #当前用户查看的试卷的发布状态 end case exercise_status when 2 ex_show_text.push("提交中") when 3 ex_show_text.push("已截止") when 4 ex_show_text.push("已结束") else ex_show_text end if is_teacher_or == 1 exercise_users_list = exercise.all_exercise_users(user.id) #当前老师所在班级的全部学生 unreview_count = exercise_users_list.exercise_unreview.size get_exercise_answers(exercise_users_list) ex_pb_time = exercise.get_exercise_times(user.id,true) exercise_publish_time = ex_pb_time[:publish_time] exercise_end_time = ex_pb_time[:end_time] current_status = 3 lock_icon = 1 #不显示锁图标 if exercise_status == 1 ex_show_text.push("未发布") elsif exercise_status == 3 ex_show_text.push("评阅中") end elsif is_teacher_or == 2 exercise_users_list = exercise.get_stu_exercise_users get_exercise_answers(exercise_users_list) # 未答和已答的 unreview_count = exercise_users_list.exercise_unreview.size ex_pb_time = exercise.get_exercise_times(user.id,false) exercise_publish_time = ex_pb_time[:publish_time] exercise_end_time = ex_pb_time[:end_time] current_status = exercise.check_user_answer_status(user) lock_icon = 1 #不显示锁图标 if current_status == 4 ex_show_text.push("未提交") end else exercise_users_list = exercise.get_stu_exercise_users get_exercise_answers(exercise_users_list) # 未答和已答的 exercise_publish_time = exercise.publish_time exercise_end_time = exercise.end_time unreview_count = nil if exercise.is_public current_status = exercise.check_user_answer_status(user) lock_icon = 1 #非课堂成员,但是试卷为公开的,不加锁 if current_status == 4 ex_show_text.push("未提交") end else current_status = 4 lock_icon = 0 #显示锁图标 end end if exercise_status > 1 show_unreview_count = unreview_count else show_unreview_count = nil end if exercise_status == 2 && exercise_end_time.present? ex_left_time = how_much_time(exercise_end_time) # elsif exercise_status == 3 && course.end_date.present? # ex_left_time = how_much_time(course.end_date.to_time) else #已截止后不显示时间 ex_left_time = nil end { "publish_time":exercise_publish_time, "end_time":exercise_end_time, "exercise_answer":@exercise_answers, "exercise_unanswer":@exercise_unanswers, "current_status":current_status, "lock_icon":lock_icon, "unreview_count": show_unreview_count, "ex_status":exercise_status, "ex_tips":ex_show_text, "ex_left_time": ex_left_time } end #计算试卷的总分和试卷的答题状态 def calculate_student_score(exercise,user) score1 = 0.0 #选择题/判断题 score2 = 0.0 #填空题 score5 = 0.0 #实训题 ques_stand = [] #问题是否正确 exercise_questions = exercise.exercise_questions.includes(:exercise_answers,:exercise_shixun_answers,:exercise_standard_answers,:exercise_shixun_challenges) exercise_questions&.each do |q| begin if q.question_type != 5 answers_content = q.exercise_answers.where(user_id: user.id) #学生的答案 else answers_content = q.exercise_shixun_answers.where(user_id: user.id) #学生的答案 end if q.question_type <= 2 #为选择题或判断题时 if answers_content.present? #学生有回答时 answer_choice_array = [] answers_content.each do |a| answer_choice_array.push(a.exercise_choice.choice_position) #学生答案的位置 end user_answer_content = answer_choice_array.sort standard_answer = q.exercise_standard_answers.pluck(:exercise_choice_id).sort #该问题的标准答案,可能有多个 if user_answer_content == standard_answer #答案一致,多选或单选才给分,答案不对不给分 if standard_answer.count > 0 q_score_1 = (q.question_score.to_f / standard_answer.count) #当多选答案正确时,每个answer的分数均摊。 else q_score_1 = 0.0 end answers_content.update_all(:score => q_score_1) score1 = score1 + q.question_score end else score1 += 0.0 end elsif q.question_type == 3 #填空题 if answers_content.present? null_standard_answer = q.exercise_standard_answers standard_answer_array = null_standard_answer.select(:exercise_choice_id,:answer_text) standard_answer_ids = standard_answer_array.pluck(:exercise_choice_id).reject(&:blank?).uniq #标准答案的exercise_choice_id数组 standard_answer_count = standard_answer_ids.count if standard_answer_count > 0 #存在标准答案时才有分数 q_score_2 = (q.question_score.to_f / standard_answer_count) #每一空的得分 else q_score_2 = 0.0 end if q.is_ordered answers_content.each do |u| i_standard_answer = standard_answer_array.where(exercise_choice_id:u.exercise_choice_id).pluck(:answer_text).reject(&:blank?).map!(&:downcase) #该选项的全部标准答案 if i_standard_answer.include?(u.answer_text.downcase) #该空的标准答案包含用户的答案才有分数 u.update_column('score',q_score_2) score2 = score2 + q_score_2 end end else st_answer_text = standard_answer_array.pluck(:answer_text).reject(&:blank?).map!(&:downcase) answers_content.each do |u| u_answer_text = u.answer_text.downcase if st_answer_text.include?(u_answer_text) #只要标准答案包含用户的答案,就有分数。同时,下一次循环时,就会删除该标准答案。防止用户的相同答案获分 u.update_column("score",q_score_2) score2 = score2 + q_score_2 st_answer_text.delete(u_answer_text) end end end else score2 += 0.0 end elsif q.question_type == 5 #实训题时,主观题这里不评分 q.exercise_shixun_challenges&.each do |exercise_cha| game = Game.user_games(user.id,exercise_cha.challenge_id)&.first #当前用户的关卡 if game.present? exercise_cha_score = 0.0 answer_status = 0 # if game.status == 2 && game.final_score >= 0 if game.final_score > 0 exercise_cha_score = game.real_score(exercise_cha.question_score) # exercise_cha_score = exercise_cha.question_score #每一关卡的得分 answer_status = 1 end ex_shixun_answer_content = answers_content&.where(exercise_shixun_challenge_id: exercise_cha.id) code = nil if exercise_cha.challenge&.path.present? cha_path = challenge_path(exercise_cha.challenge&.path) game_challenge = game.game_codes.search_challenge_path(cha_path)&.first if game_challenge.present? game_code = game_challenge code = game_code.try(:new_code) else code = git_fle_content(game.myshixun.repo_path,cha_path) end end if ex_shixun_answer_content.blank? #把关卡的答案存入试卷的实训里 ### Todo 实训题的_shixun_details里的代码是不是直接从这里取出就可以了?涉及到code的多个版本库的修改 sx_option = { :exercise_question_id => q.id, :exercise_shixun_challenge_id => exercise_cha.id, :user_id => user.id, :score => exercise_cha_score.round(1), :answer_text => code, :status => answer_status } ExerciseShixunAnswer.create(sx_option) else ex_shixun_answer_content.first.update_attributes(score:exercise_cha_score.round(1),answer_text:code) end score5 += exercise_cha_score else score5 += 0.0 end end end user_scores = answers_content.blank? ? 0.0 : answers_content.score_reviewed.pluck(:score).sum if user_scores > 0.0 stand_answer = 1 else stand_answer = 0 end ques_option = { "q_id":q.id, #该问题的id "q_type":q.question_type, "q_position":q.question_number, #该问题的位置 "stand_status":stand_answer, #该问题是否正确,1为正确,0为错误 "user_score":user_scores.round(1) #每个问题的总得分 } ques_stand.push(ques_option) rescue Exception => e Rails.logger.info("calcuclate_score_have_error____________________________#{e}") next end end total_score = score1 + score2 + score5 { "total_score":total_score.round(1), "stand_status":ques_stand } end #获取用户的相关信息 def exercise_use_info(ex_user,user_status,exercise) course = exercise.course current_user_group_id = "" current_user_group_name = "" ex_user_user = ex_user.user exercise_user_name = ex_user_user.real_name exercise_user_id = ex_user_user.id ex_user_exercise_status = exercise.get_exercise_status(exercise_user_id) ex_user_student_id = ex_user_user.student_id if ex_user.start_at.present? && ex_user.commit_status == 0 #用户已回答,但未提交 commit_status = 2 #继续答题 else commit_status = ex_user.commit_status end ex_user_end_at = ex_user.end_at course_member = course.students.course_find_by_ids("user_id",ex_user.user.id) current_user_group_id = course_member.first.course_group_id if course_member.present? if current_user_group_id == 0 current_user_group_name = "未分班" else course_group = course.course_groups.by_group_ids(current_user_group_id) current_user_group_name = course_group.first.name if course_group.present? end teacher_review = ex_user.subjective_score < 0.0 ? false : true if (user_status == 0 && commit_status == 1) || (user_status == 1 && ex_user_exercise_status == 3 && commit_status == 1) #老师都可以看,学生,需在试卷已提交,且已截止的情况下看 ex_object_score = ex_user.objective_score < 0.0 ? 0.0 : ex_user.objective_score.round(1).to_s ex_subject_score = ex_user.subjective_score < 0.0 ? nil : ex_user.subjective_score.round(1).to_s score = ex_user.score.present? ? ex_user.score.round(1).to_s : 0.0.to_s else ex_object_score = nil ex_subject_score = nil score = nil end { "user_name":exercise_user_name, "login":ex_user_user.login, "user_group_id":current_user_group_id, "user_group_name":current_user_group_name, "teacher_review":teacher_review, "ex_object_score":ex_object_score, "ex_subject_score":ex_subject_score, "score":score, "user_id":exercise_user_id, "commit_status":commit_status, "student_id":ex_user_student_id, "end_at":ex_user_end_at } end #公用tab页的相关信息 def ex_common_header(is_teacher_or,exercise) exercise_url_status = [] if is_teacher_or == 1 #当为老师的 common_tabs = %w(1 2 3 4) else if exercise.show_statistic #开启了公开统计 common_tabs = %w(1 2) else common_tabs = %w(1) end end common_tabs.each do |c| if request.url.include?("exercise_lists") active_status = 1 elsif request.url.include?("exercises_result") active_status = 1 elsif request.url.include?("exercise_setting") active_status = 1 elsif request.url == exercise_path(exercise) active_status = 1 else active_status = 0 end common_tab = { :common_tab => c, :active_status => active_status } exercise_url_status.push(common_tab) end exercise_url_status end #试卷/问卷 设置页面的发布规则返回内容 def get_user_setting_course(user_published_setting,user_course_groups) loop_course_publish_time = [] total_array_groups = [] user_published_setting.each do |u| new_json_array_group = {} setting_group_name = user_course_groups.detect{|g| g[:group_id] == u.course_group_id} if loop_course_publish_time.length > 0 time_length = loop_course_publish_time.length #问卷发布时间和截止时间相同的集合 (1..time_length).each do |i| if (loop_course_publish_time[i-1][:publish_time] == u.publish_time) && (loop_course_publish_time[i-1][:end_time] == u.end_time) #当起止时间已存在时,直接更新 loop_course_ids = total_array_groups[i-1][:course_group_id] +[u.course_group_id] loop_course_names = total_array_groups[i-1][:course_group_name] + [setting_group_name[:group_name]] new_json_array_group = { "loop_times":i-1, "course_group_id":loop_course_ids, "course_group_name":loop_course_names, } end end if new_json_array_group.length > 0 loop_times = new_json_array_group[:loop_times] total_array_groups[loop_times][:course_group_id] = new_json_array_group[:course_group_id] total_array_groups[loop_times][:course_group_name] = new_json_array_group[:course_group_name] else loop_course_times = { "publish_time":u.publish_time, "end_time": u.end_time } loop_course_publish_time.push(loop_course_times) json_array_group = { "course_group_id":[u.course_group_id], "course_group_name":[setting_group_name[:group_name]], "course_publish_time":u.publish_time, "course_end_time":u.end_time } total_array_groups.push(json_array_group) end else #第一次循环获得初始值 第一步 loop_course_times = { "publish_time":u.publish_time, "end_time": u.end_time } loop_course_publish_time.push(loop_course_times) json_array_group = { #第一个问卷发布规则的第一条记录 "course_group_id":[u.course_group_id], "course_group_name":[setting_group_name[:group_name]], "course_publish_time":u.publish_time, "course_end_time":u.end_time } total_array_groups.push(json_array_group) end end total_array_groups end #学生的分数状态及回答的内容 def user_question_answers(q,ex_answerer_id,student_status,is_teacher_or,ex_status,ques_type,ex_type) answered_content = [] user_score = nil shixun_type = 0 question_comment = [] if ques_type == 5 exercise_answers = q.exercise_shixun_answers.search_shixun_answers("user_id",ex_answerer_id) else exercise_answers = q.exercise_answers.search_exercise_answer("user_id",ex_answerer_id) #试卷用户的回答 end if student_status == 2 #当前为老师,或为学生且已提交 user_score_pre = exercise_answers.score_reviewed if ques_type == 4 && user_score_pre.blank? #主观题时,且没有大于0的分数时,为空 user_score = nil else user_score = user_score_pre.present? ? user_score_pre.pluck(:score).sum : 0.0 end end if user_score.present? && (user_score > q.question_score) user_score = q.question_score end if ques_type <= 2 answered_content = exercise_answers.pluck(:exercise_choice_id) elsif ques_type == 3 exercise_answers.each do |a| u_answer = { "choice_id":a.exercise_choice_id, "answer_text": a.answer_text } answered_content.push(u_answer) end elsif ques_type == 4 answered_content = exercise_answers.pluck(:answer_text) end if ques_type == 5 #存在实训题,及已经做了实训题的 if ex_status == 3 || is_teacher_or == 1 #如果试卷已截止,则可以看到分数,否则不能查看分数 shixun_type = 2 elsif ex_status == 2 && q.exercise_shixun_answers.present? #试卷未截止,且用户已回答的,则能看到答题的状态 shixun_type =1 end end if ex_type == 4 #填空题/主观题/实训题有评论的 q_answer_id = exercise_answers.present? ? exercise_answers.first.id : nil question_comment = q.exercise_answer_comments.search_answer_comments("exercise_answer_id",q_answer_id) end { "user_score": (user_score.present? ? user_score.round(1).to_s : nil), "answered_content":answered_content, "shixun_type":shixun_type, "question_comment":question_comment } end def convert_to_char(str) result = "" length = str.length unless str.nil? if length === 1 result += (str.to_i + 64).chr return result elsif length > 1 for i in 0...length result += (str[i].to_i + 64).chr end return result end end result end def get_exercise_left_time(exercise,user) ex_time = exercise.time user_left_time = nil time_now_i = Time.now.to_i if ex_time > 0 exercise_user = exercise.exercise_users.find_by(user_id:user.id) time_mill = ex_time * 60 #转为秒 exercise_end_time = exercise.end_time.present? ? exercise.end_time.to_i : 0 exercise_user_start = exercise_user&.start_at.present? ? exercise_user.start_at.to_i : 0 #用户未开始答题时,即exercise_user_start为0 if exercise_user_start == 0 if (exercise_end_time - time_now_i) > time_mill user_left_time = time_mill else user_left_time = (exercise_end_time < time_now_i) ? nil : (exercise_end_time - time_now_i) end else if (exercise_user_start + time_mill) > exercise_end_time time_mill = exercise_end_time - exercise_user_start #如果开始答题时间加试卷的限时长大于试卷的截止时间,则以试卷的截止时间到开始答题时间为试卷的限时 end exercise_user_left_time = time_now_i - exercise_user_start #用户已回答的时间 user_left_time = (time_mill < exercise_user_left_time) ? nil : (time_mill - exercise_user_left_time) #当前用户对试卷的回答剩余时间 end end user_left_time end #实训题学生代码的行数 def content_line(content) content.split(/\r?\n/).length end end