FEATURE: Order by emotion on /filter (#913)

This commit is contained in:
Rafael dos Santos Silva 2024-11-14 12:45:40 -03:00 committed by GitHub
parent 823e8ef490
commit 5026ab52d0
No known key found for this signature in database
GPG Key ID: B5690EEEBB952194
3 changed files with 288 additions and 0 deletions

View File

@ -0,0 +1,76 @@
# frozen_string_literal: true
module DiscourseAi
module Sentiment
class EmotionFilterOrder
def self.register!(plugin)
emotions = %w[
admiration
amusement
anger
annoyance
approval
caring
confusion
curiosity
desire
disappointment
disapproval
disgust
embarrassment
excitement
fear
gratitude
grief
joy
love
nervousness
neutral
optimism
pride
realization
relief
remorse
sadness
surprise
]
emotions.each do |emotion|
filter_order_emotion = ->(scope, order_direction) do
emotion_clause = <<~SQL
SUM(
CASE
WHEN (classification_results.classification::jsonb->'#{emotion}')::float > 0.1
THEN 1
ELSE 0
END
)::float / COUNT(posts.id)
SQL
scope
.joins(:posts)
.joins(<<~SQL)
INNER JOIN classification_results
ON classification_results.target_id = posts.id
AND classification_results.target_type = 'Post'
AND classification_results.model_used = 'SamLowe/roberta-base-go_emotions'
SQL
.where(<<~SQL)
topics.archetype = 'regular'
AND topics.deleted_at IS NULL
AND posts.deleted_at IS NULL
AND posts.post_type = 1
SQL
.select(<<~SQL)
topics.*,
#{emotion_clause} AS emotion_#{emotion}
SQL
.group("1")
.having("#{emotion_clause} > 0.05")
.order("#{emotion_clause} #{order_direction}")
end
plugin.add_filter_custom_filter("order:emotion_#{emotion}", &filter_order_emotion)
end
end
end
end
end

View File

@ -14,6 +14,8 @@ module DiscourseAi
plugin.on(:post_created, &sentiment_analysis_cb)
plugin.on(:post_edited, &sentiment_analysis_cb)
EmotionFilterOrder.register!(plugin)
plugin.add_report("overall_sentiment") do |report|
report.modes = [:stacked_chart]
threshold = 0.6

View File

@ -0,0 +1,210 @@
# frozen_string_literal: true
require "rails_helper"
RSpec.describe DiscourseAi::Sentiment::EmotionFilterOrder do
let(:plugin) { Plugin::Instance.new }
let(:model_used) { "SamLowe/roberta-base-go_emotions" }
let(:post_1) { Fabricate(:post) }
let(:post_2) { Fabricate(:post) }
let(:post_3) { Fabricate(:post) }
let(:classification_1) do
{
love: 0.9444406,
admiration: 0.013724019,
surprise: 0.010188869,
excitement: 0.007888741,
curiosity: 0.006301749,
joy: 0.004060776,
confusion: 0.0028238264,
approval: 0.0018160914,
realization: 0.001174849,
neutral: 0.0008561869,
amusement: 0.00075853954,
disapproval: 0.0006987994,
disappointment: 0.0006166883,
anger: 0.0006000542,
annoyance: 0.0005615011,
desire: 0.00046368592,
fear: 0.00045117878,
sadness: 0.00041727215,
gratitude: 0.00041727215,
optimism: 0.00037112957,
disgust: 0.00035552034,
nervousness: 0.00022954118,
embarrassment: 0.0002049572,
caring: 0.00017737568,
remorse: 0.00011407586,
grief: 0.0001006716,
pride: 0.00009681493,
relief: 0.00008919009,
}
end
let(:classification_2) do
{
love: 0.8444406,
admiration: 0.113724019,
surprise: 0.010188869,
excitement: 0.007888741,
curiosity: 0.006301749,
joy: 0.004060776,
confusion: 0.0028238264,
approval: 0.0018160914,
realization: 0.001174849,
neutral: 0.0008561869,
amusement: 0.00075853954,
disapproval: 0.0006987994,
disappointment: 0.0006166883,
anger: 0.0006000542,
annoyance: 0.0005615011,
desire: 0.00046368592,
fear: 0.00045117878,
sadness: 0.00041727215,
gratitude: 0.00041727215,
optimism: 0.00037112957,
disgust: 0.00035552034,
nervousness: 0.00022954118,
embarrassment: 0.0002049572,
caring: 0.00017737568,
remorse: 0.00011407586,
grief: 0.0001006716,
pride: 0.00009681493,
relief: 0.00008919009,
}
end
let(:classification_3) do
{
anger: 0.8503682,
annoyance: 0.08113059,
disgust: 0.020593312,
disapproval: 0.013718102,
neutral: 0.0074148285,
disappointment: 0.005785964,
sadness: 0.0028253668,
curiosity: 0.0028253668,
confusion: 0.0023885092,
surprise: 0.001524171,
embarrassment: 0.0012784768,
love: 0.001177788,
admiration: 0.0010892758,
realization: 0.001080799,
approval: 0.00102328,
fear: 0.00097261387,
amusement: 0.0007724123,
excitement: 0.00059921003,
gratitude: 0.00055852515,
joy: 0.00054986606,
optimism: 0.00050458545,
desire: 0.00046849172,
caring: 0.00037205798,
remorse: 0.00028415458,
grief: 0.00025973833,
nervousness: 0.00024305031,
pride: 0.00011661681,
relief: 0.00007470753,
}
end
let!(:classification_result_1) do
Fabricate(
:sentiment_classification,
target: post_1,
model_used: model_used,
classification: classification_1,
)
end
let!(:classification_result_2) do
Fabricate(
:sentiment_classification,
target: post_2,
model_used: model_used,
classification: classification_2,
)
end
let!(:classification_result_3) do
Fabricate(
:sentiment_classification,
target: post_3,
model_used: model_used,
classification: classification_3,
)
end
before { described_class.register!(plugin) }
it "registers emotion filters" do
emotions = %w[
disappointment
sadness
annoyance
neutral
disapproval
realization
nervousness
approval
joy
anger
embarrassment
caring
remorse
disgust
grief
confusion
relief
desire
admiration
optimism
fear
love
excitement
curiosity
amusement
surprise
gratitude
pride
]
filters = DiscoursePluginRegistry.custom_filter_mappings.reduce(Hash.new, :merge)
emotions.each { |emotion| expect(filters).to include("order:emotion_#{emotion}") }
end
it "filters topics by emotion" do
emotion = "joy"
scope = Topic.all
order_direction = "desc"
filter =
DiscoursePluginRegistry
.custom_filter_mappings
.find { _1.keys.include? "order:emotion_#{emotion}" }
.values
.first
result = filter.call(scope, order_direction)
expect(result.to_sql).to include("INNER JOIN classification_results")
expect(result.to_sql).to include(
"classification_results.model_used = 'SamLowe/roberta-base-go_emotions'",
)
expect(result.to_sql).to include("topics.archetype = 'regular'")
expect(result.to_sql).to include("ORDER BY")
expect(result.to_sql).to include("->'#{emotion}'")
expect(result.to_sql).to include("desc")
end
it "sorts emotion in ascending order" do
expect(
TopicsFilter
.new(guardian: Guardian.new)
.filter_from_query_string("order:emotion_love-asc")
.pluck(:id),
).to contain_exactly(post_2.topic.id, post_1.topic.id)
end
it "sorts emotion in default descending order" do
expect(
TopicsFilter
.new(guardian: Guardian.new)
.filter_from_query_string("order:emotion_love")
.pluck(:id),
).to contain_exactly(post_1.topic.id, post_2.topic.id)
end
end