FIX: more accurate and flexible random assign automation (#222)

FIX: more accurate and flexible random assign automation (#222)

  • allows to define the number of days within which a user is considered recently assigned
  • more correct by now using post custom fields
diff --git a/config/locales/client.en.yml b/config/locales/client.en.yml
index 46aa4a6..cc0751d 100644
--- a/config/locales/client.en.yml
+++ b/config/locales/client.en.yml
@@ -86,7 +86,13 @@ en:
             assignees_group:
               label: Assignees Group
             minimum_time_between_assignments:
-              label: Hours between assignments
+              label: Mininmum hours between assignments
+            min_recently_assigned_days:
+              label: Min recently assigned days
+              description: Defaults to 14 days.
+            max_recently_assigned_days:
+              label: Max recently assigned days
+              description: First attempt to exclude users assigned in the last `n` days. If no user left, fallbacks to `min_recently_assigned_days`. Defaults to 180 days.
             assigned_topic:
               label: Assigned Topic ID
             in_working_hours:
diff --git a/lib/random_assign_utils.rb b/lib/random_assign_utils.rb
index 0360e9d..68a8766 100644
--- a/lib/random_assign_utils.rb
+++ b/lib/random_assign_utils.rb
@@ -1,6 +1,16 @@
 # frozen_string_literal: true
 
 class RandomAssignUtils
+  def self.recently_assigned_users_ids(topic_id, from)
+    posts = Post
+      .joins(:user)
+      .where(topic_id: topic_id, action_code: 'assigned')
+      .where('posts.created_at > ?', from)
+      .order(created_at: :desc)
+    usernames = Post.custom_fields_for_ids(posts, [:action_code_who]).map { |_, v| v['action_code_who'] }.uniq
+    User.where(username: usernames).limit(100).pluck(:id)
+  end
+
   def self.user_tzinfo(user_id)
     timezone = UserOption.where(user_id: user_id).pluck(:timezone).first || "UTC"
 
diff --git a/plugin.rb b/plugin.rb
index 1634cb3..e3c4f53 100644
--- a/plugin.rb
+++ b/plugin.rb
@@ -701,6 +701,8 @@ after_initialize do
       field :assignees_group, component: :group
       field :assigned_topic, component: :text
       field :minimum_time_between_assignments, component: :text
+      field :max_recently_assigned_days, component: :text
+      field :min_recently_assigned_days, component: :text
       field :in_working_hours, component: :boolean
 
       version 1
@@ -719,7 +721,7 @@ after_initialize do
         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')
+        min_hours = fields.dig('minimum_time_between_assignments', 'value').presence
         if min_hours && TopicCustomField
             .where(name: 'assigned_to_id', topic_id: topic_id)
             .where('created_at < ?', min_hours.to_i.hours.ago)
@@ -747,23 +749,14 @@ after_initialize do
           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
+        last_assignees_ids = RandomAssignUtils.recently_assigned_users_ids(
+          topic_id,
+          (fields.dig('max_recently_assigned_days', 'value').presence || 180).to_i.days.ago
+        )
 
         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
+          recently_assigned_users_ids = RandomAssignUtils.recently_assigned_users_ids(topic_id, (fields.dig('min_recently_assigned_days', 'value').presence || 14).to_i.days.ago)
           users_ids = group_users_ids - recently_assigned_users_ids
         end
 
diff --git a/spec/lib/random_assign_utils_spec.rb b/spec/lib/random_assign_utils_spec.rb
new file mode 100644
index 0000000..129d7ed
--- /dev/null
+++ b/spec/lib/random_assign_utils_spec.rb
@@ -0,0 +1,45 @@
+# frozen_string_literal: true
+
+require 'rails_helper'
+require_relative '../support/assign_allowed_group'
+require 'random_assign_utils'
+
+describe RandomAssignUtils do
+  before do
+    SiteSetting.assign_enabled = true
+  end
+
+  let(:post) { Fabricate(:post) }
+
+  describe '.recently_assigned_users_ids' do
+    context 'no one has been assigned' do
+      it 'returns an empty array' do
+        assignees_ids = described_class.recently_assigned_users_ids(post.topic_id, 2.months.ago)
+        expect(assignees_ids).to eq([])
+      end
+    end
+
+    context 'users have been assigned' do
+      let(:admin) { Fabricate(:admin) }
+      let(:assign_allowed_group) { Group.find_by(name: 'staff') }
+      let(:user_1) { Fabricate(:user, groups: [assign_allowed_group]) }
+      let(:user_2) { Fabricate(:user, groups: [assign_allowed_group]) }
+      let(:user_3) { Fabricate(:user, groups: [assign_allowed_group]) }
+
+      it 'returns the recently assigned user ids' do
+        freeze_time 1.months.ago do
+          Assigner.new(post.topic, admin).assign(user_1)
+          Assigner.new(post.topic, admin).assign(user_2)
+        end
+
+        freeze_time 3.months.ago do
+          Assigner.new(post.topic, admin).assign(user_3)
+        end
+
+        assignees_ids = described_class.recently_assigned_users_ids(post.topic_id, 2.months.ago)
+
+        expect(assignees_ids).to contain_exactly(user_1.id, user_2.id)
+      end
+    end
+  end
+end

GitHub sha: 7747bb81a08c68f064ba940165a99733d2aab600

This commit appears in #222 which was approved by ZogStriP. It was merged by jjaffeux.