157 lines
4.9 KiB
Ruby
157 lines
4.9 KiB
Ruby
# frozen_string_literal: true
|
|
|
|
##
|
|
# Summarize the workload of a whole group and integrate its members including
|
|
# the group user dummy.
|
|
#
|
|
class GroupWorkload
|
|
attr_reader :time_span, :user_workload
|
|
|
|
##
|
|
# @param users [WlUserSelection] Users given as WlUserSelection object.
|
|
# @param user_workload [UserWorkload] User workload given as UserWorkload object.
|
|
# @param time_span [Range] A time span given as Range object.
|
|
#
|
|
def initialize(users:, user_workload:, time_span:)
|
|
self.users = users
|
|
self.user_workload = user_workload
|
|
self.time_span = time_span
|
|
self.selected_groups = define_selected_groups
|
|
self.group_members = define_group_members
|
|
end
|
|
|
|
##
|
|
# Gives all aggregated data of the group and details for each user.
|
|
#
|
|
# @return [Hash(Group, UserWorkload#hours_per_user_issue_and_day)] Hash with
|
|
# results of UserWorkload#hours_per_user_issue_and_day for each group.
|
|
def by_group
|
|
selected_groups&.each_with_object({}) do |group, hash|
|
|
summary = summarize_over_group_members(group)
|
|
hash[group] = summary.merge(group_members[group])
|
|
end
|
|
end
|
|
|
|
private
|
|
|
|
attr_accessor :users, :selected_groups, :group_members
|
|
attr_writer :time_span, :user_workload
|
|
|
|
def define_selected_groups
|
|
users.groups&.selected
|
|
end
|
|
|
|
##
|
|
# Select only those group members having their main group equal to the group
|
|
# given.
|
|
#
|
|
def define_group_members
|
|
selected_groups&.each_with_object({}) do |group, hash|
|
|
hash[group] = sorted_user_workload.select { |user, _data| user.main_group_id == group.id }
|
|
end
|
|
end
|
|
|
|
##
|
|
# Sorting of users lastname and their class name in order to ensure that the
|
|
# GroupUserDummy will come first.
|
|
#
|
|
def sorted_user_workload
|
|
user_workload_with_availabilities.sort_by { |user, _data| [user.class.name, user.lastname] }.to_h
|
|
end
|
|
|
|
##
|
|
# Adds those users which are selected but not considered for the workload
|
|
# table since they have no issues assigned yet.
|
|
#
|
|
def user_workload_with_availabilities
|
|
availabilities = users.selected - assignees
|
|
availabilities.each do |user|
|
|
user_workload[user] = { total: total_availabilities_of(user) }
|
|
end
|
|
user_workload
|
|
end
|
|
|
|
def total_availabilities_of(user)
|
|
working_days = WlDateTools.working_days_in_time_span(time_span, user)
|
|
time_span.each_with_object({}) do |day, hash|
|
|
holiday = working_days.exclude?(day)
|
|
capacity = WlDayCapacity.new(assignee: user)
|
|
hash[day] = {}
|
|
hash[day][:hours] = 0.0
|
|
hash[day][:holiday] = holiday
|
|
hash[day][:lowload] = capacity.threshold_at(:lowload, holiday)
|
|
hash[day][:normalload] = capacity.threshold_at(:normalload, holiday)
|
|
hash[day][:highload] = capacity.threshold_at(:highload, holiday)
|
|
end
|
|
end
|
|
|
|
##
|
|
# Users having issues assigned and are therefore considered in user_workload
|
|
# calculation.
|
|
#
|
|
def assignees
|
|
user_workload.keys
|
|
end
|
|
|
|
def summarize_over_group_members(group)
|
|
{ overdue_hours: sum_of(:overdue_hours, group),
|
|
overdue_number: sum_of(:overdue_number, group),
|
|
unscheduled_number: sum_of(:unscheduled_number, group),
|
|
unscheduled_hours: sum_of(:unscheduled_hours, group),
|
|
total: total_of_group_members(group),
|
|
invisible: invisibles_of_group_members(group) }
|
|
end
|
|
|
|
def sum_of(key, group)
|
|
group_members[group].sum { |_member, data| data[key.to_sym] || 0 }
|
|
end
|
|
|
|
def total_of_group_members(group)
|
|
time_span.each_with_object({}) do |day, hash|
|
|
hash[day] = {}
|
|
hash[day][:hours] = hours_at(day, :total, group)
|
|
hash[day][:holiday] = holiday_at(day, :total, group)
|
|
hash[day][:lowload] = threshold_at(day, :lowload, group)
|
|
hash[day][:normalload] = threshold_at(day, :normalload, group)
|
|
hash[day][:highload] = threshold_at(day, :highload, group)
|
|
end
|
|
end
|
|
|
|
def invisibles_of_group_members(group)
|
|
invisible = time_span.each_with_object({}) do |day, hash|
|
|
hours = hours_at(day, :invisible, group)
|
|
holidays = holiday_at(day, :invisible, group)
|
|
|
|
hash[day] = {}
|
|
hash[day][:hours] = hours
|
|
hash[day][:holiday] = holidays
|
|
end
|
|
invisible.any? { |_date, data| data[:hours].positive? } ? invisible : nil
|
|
end
|
|
|
|
def hours_at(day, key, group)
|
|
group_members[group].sum { |_member, data| data.dig(key.to_sym, day, :hours) || 0 }
|
|
end
|
|
|
|
##
|
|
# Checks for holiday of group members including GroupUserDummy, who never will
|
|
# be on vacation, for a given day and returns true if all group members are in
|
|
# holiday at a given day or false if not.
|
|
#
|
|
def holiday_at(day, key, group)
|
|
values = group_members[group].map do |_member, data|
|
|
data.dig(key.to_sym, day, :holiday)
|
|
end
|
|
values.compact.all?
|
|
end
|
|
|
|
##
|
|
# Sums up threshold values per day and group but ignores GroupUserDummy.
|
|
#
|
|
def threshold_at(day, key, group)
|
|
group_members[group].sum do |member, data|
|
|
member.is_a?(User) ? (data.dig(:total, day, key.to_sym) || 0.0) : 0.0
|
|
end
|
|
end
|
|
end
|