diff --git a/config/locales/client.en.yml b/config/locales/client.en.yml index 260bab0..0db7639 100644 --- a/config/locales/client.en.yml +++ b/config/locales/client.en.yml @@ -75,5 +75,7 @@ en: fields: assignees_group: label: Assignees Group + minimum_time_between_assignments: + label: Hours between assignments assigned_topic: label: Assigned Topic ID diff --git a/config/locales/server.en.yml b/config/locales/server.en.yml index 603d0c1..c19ebb6 100644 --- a/config/locales/server.en.yml +++ b/config/locales/server.en.yml @@ -66,3 +66,4 @@ en: scriptables: random_assign: title: Random assign + no_one: "Attempted randomly assign a member of @%{group}, but no one was available." diff --git a/lib/random_assign_utils.rb b/lib/random_assign_utils.rb new file mode 100644 index 0000000..0360e9d --- /dev/null +++ b/lib/random_assign_utils.rb @@ -0,0 +1,37 @@ +# frozen_string_literal: true + +class RandomAssignUtils + def self.user_tzinfo(user_id) + timezone = UserOption.where(user_id: user_id).pluck(:timezone).first || "UTC" + + tzinfo = nil + begin + tzinfo = ActiveSupport::TimeZone.find_tzinfo(timezone) + rescue TZInfo::InvalidTimezoneIdentifier + Rails.logger.warn("#{User.find_by(id: user_id)&.username} has the timezone #{timezone} set, we do not know how to parse it in Rails (assuming UTC)") + timezone = "UTC" + tzinfo = ActiveSupport::TimeZone.find_tzinfo(timezone) + end + + tzinfo + end + + def self.no_one!(topic_id, group) + PostCreator.create!( + Discourse.system_user, + topic_id: topic_id, + raw: I18n.t("discourse_automation.scriptables.random_assign.no_one", group: group), + validate: false + ) + end + + def self.in_working_hours?(user_id) + tzinfo = RandomAssignUtils.user_tzinfo(user_id) + tztime = tzinfo.now + + !tztime.saturday? && + !tztime.sunday? && + tztime.hour > 7 && + tztime.hour < 11 + end +end diff --git a/plugin.rb b/plugin.rb index bb6fa4e..8a6d27c 100644 --- a/plugin.rb +++ b/plugin.rb @@ -539,9 +539,12 @@ after_initialize do end if defined?(DiscourseAutomation) + require 'random_assign_utils' + add_automation_scriptable('random_assign') do field :assignees_group, component: :group field :assigned_topic, component: :text + field :minimum_time_between_assignments, component: :text version 1 @@ -550,14 +553,76 @@ after_initialize do script do |context, fields| next unless SiteSetting.assign_enabled? - next unless group_id = fields.dig('assignees_group', 'value') - next unless group = Group.find_by(id: group_id) - assign_to = group.group_users.order(Arel.sql('RANDOM()')).first.user - next unless topic_id = fields.dig('assigned_topic', 'value') next unless topic = Topic.find_by(id: topic_id) - TopicAssigner.new(topic, Discourse.system_user).assign(assign_to) + next unless group_id = fields.dig('assignees_group', 'value') + next unless group = Group.find_by(id: group_id) + + min_hours = (fields.dig('minimum_time_between_assignments', 'value') || 12).to_i + if TopicCustomField + .where(name: 'assigned_to_id', topic_id: topic_id) + .where('created_at < ?', min_hours.hours.ago) + .exists? + next + end + + users_on_holiday = Set.new( + User + .where(id: + UserCustomField + .where(name: 'on_holiday', value: 't') + .pluck(:user_id) + ).pluck(:id) + ) + + group_users_ids = group + .group_users + .joins(:user) + .pluck('users.id') + .reject { |user_id| users_on_holiday.include?(user_id) } + + if group_users_ids.empty? + RandomAssignUtils.no_one!(topic_id, group.name) + next + end + + last_assignees_ids = UserAction + .joins(:user) + .where(action_type: UserAction::ASSIGNED, target_topic_id: topic_id) + .where('user_actions.created_at > ?', 6.months.ago) + .order(created_at: :desc) + .limit(group_users_ids.length) + .pluck('users.id') + .uniq + + users_ids = group_users_ids - last_assignees_ids + if users_ids.blank? + recently_assigned_users_ids = UserAction + .joins(:user) + .where(action_type: UserAction::ASSIGNED, target_topic_id: topic_id) + .where('user_actions.created_at < ?', 2.weeks.ago) + .pluck('users.id') + .uniq + users_ids = group_users_ids - recently_assigned_users_ids + end + + if users_ids.blank? + RandomAssignUtils.no_one!(topic_id, group.name) + next + end + + assign_to_user_id = users_ids.shuffle.find do |user_id| + RandomAssignUtils.in_working_hours?(user_id) + end + + if assign_to_user_id.blank? + RandomAssignUtils.no_one!(topic_id, group.name) + next + end + + assign_to = User.find_by(id: assign_to_user_id) + assign_to && TopicAssigner.new(topic, Discourse.system_user).assign(assign_to) end end end