You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
educoder/app/services/ecs/query_course_evaluation_ser...

114 lines
4.3 KiB

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