app/models/concerns/course_user/staff_concern.rb
# frozen_string_literal: true
# This concern related to staff performance calculation.
module CourseUser::StaffConcern
extend ActiveSupport::Concern
included do
# Sort the staff by their average marking time.
# Note that nil time will be considered as the largest, which will come to the bottom of the
# list.
#
# @param [Array<CourseUser>] staff Course users to be sorted by average marking time.
# @return [Array<CourseUser>] Course users sorted by average marking time.
def self.order_by_average_marking_time(staff)
staff.sort do |x, y|
if x.average_marking_time && y.average_marking_time
x.average_marking_time <=> y.average_marking_time
else
x.average_marking_time ? -1 : 1
end
end
end
end
# Returns the published submissions for the purpose of calculating marking statistics.
#
# This inlcudes only submissions from non-phantom, student course_users.
def published_submissions
@published_submissions ||=
Course::Assessment::Submission.
joins(experience_points_record: :course_user).
where('course_users.role = ?', CourseUser.roles[:student]).
where('course_users.phantom = ?', false).
where('course_assessment_submissions.publisher_id = ?', user_id).
where('course_users.course_id = ?', course_id).
pluck(:published_at, :submitted_at).
map { |published_at, submitted_at| { published_at: published_at, submitted_at: submitted_at } }
end
# Returns the average marking time of the staff.
#
# @return [Float] Time in seconds.
def average_marking_time
@average_marking_time ||=
if valid_submissions.empty?
nil
else
valid_submissions.sum { |s| s[:published_at] - s[:submitted_at] } / valid_submissions.size
end
end
# Returns the standard deviation of the marking time of the staff.
#
# @return [Float]
def marking_time_stddev
# An array of time in seconds.
time_diff = valid_submissions.map { |s| s[:published_at] - s[:submitted_at] }
standard_deviation(time_diff)
end
private
def valid_submissions
@valid_submissions ||=
published_submissions.
select { |s| s[:submitted_at] && s[:published_at] && s[:published_at] > s[:submitted_at] }
end
# Calculate the standard deviation of an array of time.
def standard_deviation(array)
return nil if array.empty?
Math.sqrt(sample_variance(array))
end
def mean(array)
array.sum / array.length.to_f
end
def sample_variance(array)
m = mean(array)
sum = array.reduce(0) { |acc, elem| acc + ((elem - m)**2) }
sum / array.length.to_f
end
end