1
1
# frozen_string_literal: true
2
2
3
3
class RandomAssignUtils
4
- def self . raise_error ( automation , message )
5
- raise ( "[discourse-automation id=#{ automation . id } ] #{ message } ." )
6
- end
4
+ attr_reader :context , :fields , :automation , :topic , :group
7
5
8
- def self . log_info ( automation , message )
9
- Rails . logger . info ( "[discourse-automation id= #{ automation . id } ] #{ message } ." )
6
+ def self . automation_script! ( ... )
7
+ new ( ... ) . automation_script!
10
8
end
11
9
12
- def self . automation_script! ( context , fields , automation )
13
- raise_error ( automation , "discourse-assign is not enabled" ) unless SiteSetting . assign_enabled?
10
+ def initialize ( context , fields , automation )
11
+ @context = context
12
+ @fields = fields
13
+ @automation = automation
14
14
15
+ raise_error ( "discourse-assign is not enabled" ) unless SiteSetting . assign_enabled?
15
16
unless topic_id = fields . dig ( "assigned_topic" , "value" )
16
- raise_error ( automation , "`assigned_topic` not provided" )
17
- end
18
-
19
- unless topic = Topic . find_by ( id : topic_id )
20
- raise_error ( automation , "Topic(#{ topic_id } ) not found" )
17
+ raise_error ( "`assigned_topic` not provided" )
21
18
end
22
-
23
- min_hours = fields . dig ( "minimum_time_between_assignments" , "value" ) . presence
24
- if min_hours &&
25
- TopicCustomField
26
- . where ( name : "assigned_to_id" , topic_id : topic_id )
27
- . where ( "created_at < ?" , min_hours . to_i . hours . ago )
28
- . exists?
29
- log_info ( automation , "Topic(#{ topic_id } ) has already been assigned recently" )
30
- return
19
+ unless @topic = Topic . find_by ( id : topic_id )
20
+ raise_error ( "Topic(#{ topic_id } ) not found" )
31
21
end
32
22
33
23
unless group_id = fields . dig ( "assignees_group" , "value" )
34
- raise_error ( automation , "`assignees_group` not provided" )
24
+ raise_error ( "`assignees_group` not provided" )
35
25
end
36
-
37
- unless group = Group . find_by ( id : group_id )
38
- raise_error ( automation , "Group(#{ group_id } ) not found" )
26
+ unless @group = Group . find_by ( id : group_id )
27
+ raise_error ( "Group(#{ group_id } ) not found" )
39
28
end
29
+ end
40
30
41
- assignable_user_ids = User . assign_allowed . pluck ( :id )
42
- users_on_holiday =
43
- Set . new (
44
- User . where (
45
- id : UserCustomField . where ( name : "on_holiday" , value : "t" ) . select ( :user_id ) ,
46
- ) . pluck ( :id ) ,
31
+ def automation_script!
32
+ return log_info ( "Topic(#{ topic . id } ) has already been assigned recently" ) if assigned_recently?
33
+ return no_one! unless assigned_user
34
+ assign_user!
35
+ end
36
+
37
+ def recently_assigned_users_ids ( from )
38
+ usernames =
39
+ PostCustomField
40
+ . joins ( :post )
41
+ . where (
42
+ name : "action_code_who" ,
43
+ posts : {
44
+ topic : topic ,
45
+ action_code : %w[ assigned reassigned assigned_to_post ] ,
46
+ } ,
47
+ )
48
+ . where ( "posts.created_at > ?" , from )
49
+ . order ( "posts.created_at DESC" )
50
+ . pluck ( :value )
51
+ . uniq
52
+ User
53
+ . where ( username : usernames )
54
+ . joins (
55
+ "JOIN unnest('{#{ usernames . join ( "," ) } }'::text[]) WITH ORDINALITY t(username, ord) USING(username)" ,
47
56
)
57
+ . limit ( 100 )
58
+ . order ( "ord" )
59
+ . pluck ( :id )
60
+ end
48
61
49
- group_users = group . group_users . joins ( :user )
50
- if skip_new_users_for_days = fields . dig ( "skip_new_users_for_days" , "value" ) . presence
51
- group_users = group_users . where ( "users.created_at < ?" , skip_new_users_for_days . to_i . days . ago )
52
- end
62
+ private
63
+
64
+ def assigned_user
65
+ @assigned_user ||=
66
+ begin
67
+ group_users_ids = group_users . pluck ( :id )
68
+ return if group_users_ids . empty?
69
+
70
+ last_assignees_ids = recently_assigned_users_ids ( max_recently_assigned_days )
71
+ users_ids = group_users_ids - last_assignees_ids
72
+ if users_ids . blank?
73
+ recently_assigned_users_ids = recently_assigned_users_ids ( min_recently_assigned_days )
74
+ users_ids = group_users_ids - recently_assigned_users_ids
75
+ end
76
+ users_ids << last_assignees_ids . intersection ( group_users_ids ) . last if users_ids . blank?
77
+ if fields . dig ( "in_working_hours" , "value" )
78
+ assign_to_user_id = users_ids . shuffle . detect { |user_id | in_working_hours? ( user_id ) }
79
+ end
80
+ assign_to_user_id ||= users_ids . sample
81
+
82
+ User . find ( assign_to_user_id )
83
+ end
84
+ end
53
85
54
- group_users_ids =
55
- group_users
56
- . pluck ( "users.id" )
57
- . filter { |user_id | assignable_user_ids . include? ( user_id ) }
58
- . reject { |user_id | users_on_holiday . include? ( user_id ) }
86
+ def assign_user!
87
+ return create_post_template if post_template
88
+ Assigner
89
+ . new ( topic , Discourse . system_user )
90
+ . assign ( assigned_user )
91
+ . then do |result |
92
+ next if result [ :success ]
93
+ no_one!
94
+ end
95
+ end
59
96
60
- if group_users_ids . empty?
61
- RandomAssignUtils . no_one! ( topic_id , group . name )
62
- return
63
- end
97
+ def create_post_template
98
+ post =
99
+ PostCreator . new (
100
+ Discourse . system_user ,
101
+ raw : post_template ,
102
+ skip_validations : true ,
103
+ topic_id : topic . id ,
104
+ ) . create!
105
+ Assigner
106
+ . new ( post , Discourse . system_user )
107
+ . assign ( assigned_user )
108
+ . then do |result |
109
+ next if result [ :success ]
110
+ PostDestroyer . new ( Discourse . system_user , post ) . destroy
111
+ no_one!
112
+ end
113
+ end
64
114
65
- max_recently_assigned_days =
66
- ( fields . dig ( "max_recently_assigned_days" , "value" ) . presence || 180 ) . to_i . days . ago
67
- last_assignees_ids =
68
- RandomAssignUtils . recently_assigned_users_ids ( topic_id , max_recently_assigned_days )
69
- users_ids = group_users_ids - last_assignees_ids
70
- if users_ids . blank?
71
- min_recently_assigned_days =
72
- ( fields . dig ( "min_recently_assigned_days" , "value" ) . presence || 14 ) . to_i . days . ago
73
- recently_assigned_users_ids =
74
- RandomAssignUtils . recently_assigned_users_ids ( topic_id , min_recently_assigned_days )
75
- users_ids = group_users_ids - recently_assigned_users_ids
76
- end
115
+ def group_users
116
+ users =
117
+ group
118
+ . users
119
+ . where ( id : User . assign_allowed . select ( :id ) )
120
+ . where . not (
121
+ id :
122
+ User
123
+ . joins ( :_custom_fields )
124
+ . where ( user_custom_fields : { name : "on_holiday" , value : "t" } )
125
+ . select ( :id ) ,
126
+ )
127
+ return users unless skip_new_users_for_days
128
+ users . where ( "users.created_at < ?" , skip_new_users_for_days )
129
+ end
77
130
78
- if users_ids . blank?
79
- RandomAssignUtils . no_one! ( topic_id , group . name )
80
- return
81
- end
131
+ def raise_error ( message )
132
+ raise ( "[discourse-automation id=#{ automation . id } ] #{ message } ." )
133
+ end
82
134
83
- if fields . dig ( "in_working_hours" , "value" )
84
- assign_to_user_id =
85
- users_ids . shuffle . find { |user_id | RandomAssignUtils . in_working_hours? ( user_id ) }
86
- end
135
+ def log_info ( message )
136
+ Rails . logger . info ( "[discourse-automation id=#{ automation . id } ] #{ message } ." )
137
+ end
87
138
88
- assign_to_user_id ||= users_ids . sample
89
- if assign_to_user_id . blank?
90
- RandomAssignUtils . no_one! ( topic_id , group . name )
91
- return
92
- end
139
+ def no_one!
140
+ PostCreator . create! (
141
+ Discourse . system_user ,
142
+ topic_id : topic . id ,
143
+ raw : I18n . t ( "discourse_automation.scriptables.random_assign.no_one" , group : group . name ) ,
144
+ validate : false ,
145
+ )
146
+ end
93
147
94
- assign_to = User . find ( assign_to_user_id )
95
- result = nil
96
- if raw = fields . dig ( "post_template" , "value" ) . presence
97
- post =
98
- PostCreator . new (
99
- Discourse . system_user ,
100
- raw : raw ,
101
- skip_validations : true ,
102
- topic_id : topic . id ,
103
- ) . create!
104
-
105
- result = Assigner . new ( post , Discourse . system_user ) . assign ( assign_to )
106
-
107
- PostDestroyer . new ( Discourse . system_user , post ) . destroy if !result [ :success ]
108
- else
109
- result = Assigner . new ( topic , Discourse . system_user ) . assign ( assign_to )
110
- end
148
+ def assigned_recently?
149
+ return unless min_hours
150
+ TopicCustomField
151
+ . where ( name : "assigned_to_id" , topic : topic )
152
+ . where ( "created_at < ?" , min_hours )
153
+ . exists?
154
+ end
111
155
112
- RandomAssignUtils . no_one! ( topic_id , group . name ) if !result [ :success ]
156
+ def skip_new_users_for_days
157
+ days = fields . dig ( "skip_new_users_for_days" , "value" ) . presence
158
+ return unless days
159
+ days . to_i . days . ago
113
160
end
114
161
115
- def self . recently_assigned_users_ids ( topic_id , from )
116
- posts =
117
- Post
118
- . joins ( :user )
119
- . where ( topic_id : topic_id , action_code : %w[ assigned reassigned assigned_to_post ] )
120
- . where ( "posts.created_at > ?" , from )
121
- . order ( created_at : :desc )
122
- usernames =
123
- Post . custom_fields_for_ids ( posts , [ :action_code_who ] ) . map { |_ , v | v [ "action_code_who" ] } . uniq
124
- User . where ( username : usernames ) . limit ( 100 ) . pluck ( :id )
162
+ def max_recently_assigned_days
163
+ @max_days ||= ( fields . dig ( "max_recently_assigned_days" , "value" ) . presence || 180 ) . to_i . days . ago
125
164
end
126
165
127
- def self . user_tzinfo ( user_id )
166
+ def min_recently_assigned_days
167
+ @min_days ||= ( fields . dig ( "min_recently_assigned_days" , "value" ) . presence || 14 ) . to_i . days . ago
168
+ end
169
+
170
+ def post_template
171
+ @post_template ||= fields . dig ( "post_template" , "value" ) . presence
172
+ end
173
+
174
+ def min_hours
175
+ hours = fields . dig ( "minimum_time_between_assignments" , "value" ) . presence
176
+ return unless hours
177
+ hours . to_i . hours . ago
178
+ end
179
+
180
+ def in_working_hours? ( user_id )
181
+ tzinfo = user_tzinfo ( user_id )
182
+ tztime = tzinfo . now
183
+
184
+ !tztime . saturday? && !tztime . sunday? && tztime . hour > 7 && tztime . hour < 11
185
+ end
186
+
187
+ def user_tzinfo ( user_id )
128
188
timezone = UserOption . where ( user_id : user_id ) . pluck ( :timezone ) . first || "UTC"
129
189
130
190
tzinfo = nil
@@ -140,20 +200,4 @@ def self.user_tzinfo(user_id)
140
200
141
201
tzinfo
142
202
end
143
-
144
- def self . no_one! ( topic_id , group )
145
- PostCreator . create! (
146
- Discourse . system_user ,
147
- topic_id : topic_id ,
148
- raw : I18n . t ( "discourse_automation.scriptables.random_assign.no_one" , group : group ) ,
149
- validate : false ,
150
- )
151
- end
152
-
153
- def self . in_working_hours? ( user_id )
154
- tzinfo = RandomAssignUtils . user_tzinfo ( user_id )
155
- tztime = tzinfo . now
156
-
157
- !tztime . saturday? && !tztime . sunday? && tztime . hour > 7 && tztime . hour < 11
158
- end
159
203
end
0 commit comments