class Ecs::QueryCourseEvaluationService < ApplicationService attr_reader :ec_course, :evaluation_status def initialize(ec_course) @ec_course = ec_course @_course_achievement = 0 end def course_targets @_course_targets ||= calculate_course_targets end def course_achievement course_targets @_course_achievement.round(2) end def graduation_subitem_evaluations student_scores = ec_course.ec_course_student_scores.joins(:ec_course_target).group(:ec_course_target_id) student_scores = student_scores.select('AVG(score) as average_score, ec_course_target_id') student_score_map = student_scores.group_by { |item| item.ec_course_target_id } subitem_targets = ec_course.ec_graduation_subitem_course_targets .includes(ec_graduation_subitem: :ec_graduation_requirement) subitem_targets.group_by(&:ec_graduation_subitem_id).map do |_id, arr| subitem = arr.first.ec_graduation_subitem support = subitem.ec_course_supports.find_by(ec_course_id: ec_course.id) weight = support.weights.to_f objective_achievement = (weight * ec_course.ec_year.calculation_value.to_f).round(3) target_total_rates = 0 reach_real_target = 0 arr.map(&:ec_course_target).uniq.each do |target| target_total_rates += target.weight.to_f student_score = student_score_map[target.id] reach_real_target += student_score.average_score.to_f * target.weight.to_f if student_score end actually_achievement = target_total_rates.zero? ? 0 : (reach_real_target * weight) / (target_total_rates.round(3) * 100) { graduation_subitem_id: subitem.id, content: subitem.content, position: subitem.position, graduation_requirement_position: subitem.ec_graduation_requirement.position, weights: format('%.02f', weight), support_course_target_ids: arr.map(&:ec_course_target_id), objective_achievement: format('%.03f', objective_achievement), actually_achievement: format('%.03f', actually_achievement), status: achieve_status(objective_achievement, actually_achievement) } end end def score_levels_map @_score_levels_map ||= begin ec_course.ec_score_levels.each_with_object({}) do |level, obj| obj[level.id.to_s] = level.as_json(only: %i[id position score level]) end end end private def calculate_course_targets score_levels = ec_course.ec_score_levels.to_a ec_course.ec_course_targets.includes(:ec_student_score_targets).map do |course_target| data = course_target.as_json(only: %i[id position content standard_grade]) data[:weight] = course_target.weight score_targets = course_target.ec_student_score_targets.to_a data[:average_score] = score_targets.size.zero? ? 0 : score_targets.sum(&:score).fdiv(score_targets.size).round(2) data[:maximum_score] = score_targets.max_by(&:score)&.score data[:minimum_score] = score_targets.min_by(&:score)&.score data[:actually_grade] = data[:average_score] data[:status] = achieve_status(course_target.standard_grade, data[:average_score]) # 计算总评成绩 @_course_achievement += data[:average_score].to_f * course_target.weight.to_f # 计算学生成绩分布区间 data[:score_levels] = score_levels.map do |score_level| level_condition_proc = if (score_level.position - 1).zero? # 第一区间 -> (score_target){ score_target.score >= score_level.score ? 1 : 0 } elsif score_levels.position == score_levels.size # 末尾区间 -> (score_target){ score_target.score < score_level.score ? 1 : 0 } else # 中间区间 -> (score_target){ score_target.score >= score_level.score && score_target.score < score_targets[score_level.position - 1] ? 1 : 0 } end # 计算该成绩区间人数 count = score_targets.sum(&level_condition_proc) { id: score_level.id, count: count } end data end end def achieve_status(standard_value, real_value) standard_value && real_value && real_value >= standard_value ? 'achieved' : 'not_achieved' end end