Skip to content
This repository was archived by the owner on Jul 22, 2025. It is now read-only.

Commit 5026ab5

Browse files
authored
FEATURE: Order by emotion on /filter (#913)
1 parent 823e8ef commit 5026ab5

File tree

3 files changed

+288
-0
lines changed

3 files changed

+288
-0
lines changed
Lines changed: 76 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,76 @@
1+
# frozen_string_literal: true
2+
3+
module DiscourseAi
4+
module Sentiment
5+
class EmotionFilterOrder
6+
def self.register!(plugin)
7+
emotions = %w[
8+
admiration
9+
amusement
10+
anger
11+
annoyance
12+
approval
13+
caring
14+
confusion
15+
curiosity
16+
desire
17+
disappointment
18+
disapproval
19+
disgust
20+
embarrassment
21+
excitement
22+
fear
23+
gratitude
24+
grief
25+
joy
26+
love
27+
nervousness
28+
neutral
29+
optimism
30+
pride
31+
realization
32+
relief
33+
remorse
34+
sadness
35+
surprise
36+
]
37+
38+
emotions.each do |emotion|
39+
filter_order_emotion = ->(scope, order_direction) do
40+
emotion_clause = <<~SQL
41+
SUM(
42+
CASE
43+
WHEN (classification_results.classification::jsonb->'#{emotion}')::float > 0.1
44+
THEN 1
45+
ELSE 0
46+
END
47+
)::float / COUNT(posts.id)
48+
SQL
49+
scope
50+
.joins(:posts)
51+
.joins(<<~SQL)
52+
INNER JOIN classification_results
53+
ON classification_results.target_id = posts.id
54+
AND classification_results.target_type = 'Post'
55+
AND classification_results.model_used = 'SamLowe/roberta-base-go_emotions'
56+
SQL
57+
.where(<<~SQL)
58+
topics.archetype = 'regular'
59+
AND topics.deleted_at IS NULL
60+
AND posts.deleted_at IS NULL
61+
AND posts.post_type = 1
62+
SQL
63+
.select(<<~SQL)
64+
topics.*,
65+
#{emotion_clause} AS emotion_#{emotion}
66+
SQL
67+
.group("1")
68+
.having("#{emotion_clause} > 0.05")
69+
.order("#{emotion_clause} #{order_direction}")
70+
end
71+
plugin.add_filter_custom_filter("order:emotion_#{emotion}", &filter_order_emotion)
72+
end
73+
end
74+
end
75+
end
76+
end

lib/sentiment/entry_point.rb

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -14,6 +14,8 @@ def inject_into(plugin)
1414
plugin.on(:post_created, &sentiment_analysis_cb)
1515
plugin.on(:post_edited, &sentiment_analysis_cb)
1616

17+
EmotionFilterOrder.register!(plugin)
18+
1719
plugin.add_report("overall_sentiment") do |report|
1820
report.modes = [:stacked_chart]
1921
threshold = 0.6
Lines changed: 210 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,210 @@
1+
# frozen_string_literal: true
2+
3+
require "rails_helper"
4+
5+
RSpec.describe DiscourseAi::Sentiment::EmotionFilterOrder do
6+
let(:plugin) { Plugin::Instance.new }
7+
let(:model_used) { "SamLowe/roberta-base-go_emotions" }
8+
let(:post_1) { Fabricate(:post) }
9+
let(:post_2) { Fabricate(:post) }
10+
let(:post_3) { Fabricate(:post) }
11+
let(:classification_1) do
12+
{
13+
love: 0.9444406,
14+
admiration: 0.013724019,
15+
surprise: 0.010188869,
16+
excitement: 0.007888741,
17+
curiosity: 0.006301749,
18+
joy: 0.004060776,
19+
confusion: 0.0028238264,
20+
approval: 0.0018160914,
21+
realization: 0.001174849,
22+
neutral: 0.0008561869,
23+
amusement: 0.00075853954,
24+
disapproval: 0.0006987994,
25+
disappointment: 0.0006166883,
26+
anger: 0.0006000542,
27+
annoyance: 0.0005615011,
28+
desire: 0.00046368592,
29+
fear: 0.00045117878,
30+
sadness: 0.00041727215,
31+
gratitude: 0.00041727215,
32+
optimism: 0.00037112957,
33+
disgust: 0.00035552034,
34+
nervousness: 0.00022954118,
35+
embarrassment: 0.0002049572,
36+
caring: 0.00017737568,
37+
remorse: 0.00011407586,
38+
grief: 0.0001006716,
39+
pride: 0.00009681493,
40+
relief: 0.00008919009,
41+
}
42+
end
43+
let(:classification_2) do
44+
{
45+
love: 0.8444406,
46+
admiration: 0.113724019,
47+
surprise: 0.010188869,
48+
excitement: 0.007888741,
49+
curiosity: 0.006301749,
50+
joy: 0.004060776,
51+
confusion: 0.0028238264,
52+
approval: 0.0018160914,
53+
realization: 0.001174849,
54+
neutral: 0.0008561869,
55+
amusement: 0.00075853954,
56+
disapproval: 0.0006987994,
57+
disappointment: 0.0006166883,
58+
anger: 0.0006000542,
59+
annoyance: 0.0005615011,
60+
desire: 0.00046368592,
61+
fear: 0.00045117878,
62+
sadness: 0.00041727215,
63+
gratitude: 0.00041727215,
64+
optimism: 0.00037112957,
65+
disgust: 0.00035552034,
66+
nervousness: 0.00022954118,
67+
embarrassment: 0.0002049572,
68+
caring: 0.00017737568,
69+
remorse: 0.00011407586,
70+
grief: 0.0001006716,
71+
pride: 0.00009681493,
72+
relief: 0.00008919009,
73+
}
74+
end
75+
let(:classification_3) do
76+
{
77+
anger: 0.8503682,
78+
annoyance: 0.08113059,
79+
disgust: 0.020593312,
80+
disapproval: 0.013718102,
81+
neutral: 0.0074148285,
82+
disappointment: 0.005785964,
83+
sadness: 0.0028253668,
84+
curiosity: 0.0028253668,
85+
confusion: 0.0023885092,
86+
surprise: 0.001524171,
87+
embarrassment: 0.0012784768,
88+
love: 0.001177788,
89+
admiration: 0.0010892758,
90+
realization: 0.001080799,
91+
approval: 0.00102328,
92+
fear: 0.00097261387,
93+
amusement: 0.0007724123,
94+
excitement: 0.00059921003,
95+
gratitude: 0.00055852515,
96+
joy: 0.00054986606,
97+
optimism: 0.00050458545,
98+
desire: 0.00046849172,
99+
caring: 0.00037205798,
100+
remorse: 0.00028415458,
101+
grief: 0.00025973833,
102+
nervousness: 0.00024305031,
103+
pride: 0.00011661681,
104+
relief: 0.00007470753,
105+
}
106+
end
107+
let!(:classification_result_1) do
108+
Fabricate(
109+
:sentiment_classification,
110+
target: post_1,
111+
model_used: model_used,
112+
classification: classification_1,
113+
)
114+
end
115+
let!(:classification_result_2) do
116+
Fabricate(
117+
:sentiment_classification,
118+
target: post_2,
119+
model_used: model_used,
120+
classification: classification_2,
121+
)
122+
end
123+
let!(:classification_result_3) do
124+
Fabricate(
125+
:sentiment_classification,
126+
target: post_3,
127+
model_used: model_used,
128+
classification: classification_3,
129+
)
130+
end
131+
132+
before { described_class.register!(plugin) }
133+
134+
it "registers emotion filters" do
135+
emotions = %w[
136+
disappointment
137+
sadness
138+
annoyance
139+
neutral
140+
disapproval
141+
realization
142+
nervousness
143+
approval
144+
joy
145+
anger
146+
embarrassment
147+
caring
148+
remorse
149+
disgust
150+
grief
151+
confusion
152+
relief
153+
desire
154+
admiration
155+
optimism
156+
fear
157+
love
158+
excitement
159+
curiosity
160+
amusement
161+
surprise
162+
gratitude
163+
pride
164+
]
165+
166+
filters = DiscoursePluginRegistry.custom_filter_mappings.reduce(Hash.new, :merge)
167+
168+
emotions.each { |emotion| expect(filters).to include("order:emotion_#{emotion}") }
169+
end
170+
171+
it "filters topics by emotion" do
172+
emotion = "joy"
173+
scope = Topic.all
174+
order_direction = "desc"
175+
176+
filter =
177+
DiscoursePluginRegistry
178+
.custom_filter_mappings
179+
.find { _1.keys.include? "order:emotion_#{emotion}" }
180+
.values
181+
.first
182+
result = filter.call(scope, order_direction)
183+
184+
expect(result.to_sql).to include("INNER JOIN classification_results")
185+
expect(result.to_sql).to include(
186+
"classification_results.model_used = 'SamLowe/roberta-base-go_emotions'",
187+
)
188+
expect(result.to_sql).to include("topics.archetype = 'regular'")
189+
expect(result.to_sql).to include("ORDER BY")
190+
expect(result.to_sql).to include("->'#{emotion}'")
191+
expect(result.to_sql).to include("desc")
192+
end
193+
194+
it "sorts emotion in ascending order" do
195+
expect(
196+
TopicsFilter
197+
.new(guardian: Guardian.new)
198+
.filter_from_query_string("order:emotion_love-asc")
199+
.pluck(:id),
200+
).to contain_exactly(post_2.topic.id, post_1.topic.id)
201+
end
202+
it "sorts emotion in default descending order" do
203+
expect(
204+
TopicsFilter
205+
.new(guardian: Guardian.new)
206+
.filter_from_query_string("order:emotion_love")
207+
.pluck(:id),
208+
).to contain_exactly(post_1.topic.id, post_2.topic.id)
209+
end
210+
end

0 commit comments

Comments
 (0)