FEATURE: improves random assign automation (#166)
- Makes a best attempt at being random and assigning people who haven’t been assigned for a long time - Uses timezones and holidays - Allows to define a minimum delay between assignments - Creates a post if no one is available
This commit is contained in:
		
							parent
							
								
									a8495e9d7a
								
							
						
					
					
						commit
						0046041e9e
					
				| 
						 | 
				
			
			@ -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
 | 
			
		||||
| 
						 | 
				
			
			
 | 
			
		|||
| 
						 | 
				
			
			@ -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."
 | 
			
		||||
| 
						 | 
				
			
			
 | 
			
		|||
| 
						 | 
				
			
			@ -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
 | 
			
		||||
							
								
								
									
										75
									
								
								plugin.rb
								
								
								
								
							
							
						
						
									
										75
									
								
								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
 | 
			
		||||
| 
						 | 
				
			
			
 | 
			
		|||
		Loading…
	
		Reference in New Issue