discourse-ai/lib/sentiment/entry_point.rb

138 lines
4.8 KiB
Ruby

# frozen_string_literal: true
module DiscourseAi
module Sentiment
class EntryPoint
def inject_into(plugin)
sentiment_analysis_cb =
Proc.new do |post|
if SiteSetting.ai_sentiment_enabled
Jobs.enqueue(:post_sentiment_analysis, post_id: post.id)
end
end
plugin.on(:post_created, &sentiment_analysis_cb)
plugin.on(:post_edited, &sentiment_analysis_cb)
plugin.add_report("overall_sentiment") do |report|
report.modes = [:stacked_chart]
threshold = 60
sentiment_count_sql = Proc.new { |sentiment| <<~SQL }
COUNT(
CASE WHEN (cr.classification::jsonb->'#{sentiment}')::integer > :threshold THEN 1 ELSE NULL END
) AS #{sentiment}_count
SQL
grouped_sentiments =
DB.query(
<<~SQL,
SELECT
DATE_TRUNC('day', p.created_at)::DATE AS posted_at,
#{sentiment_count_sql.call("positive")},
-#{sentiment_count_sql.call("negative")}
FROM
classification_results AS cr
INNER JOIN posts p ON p.id = cr.target_id AND cr.target_type = 'Post'
INNER JOIN topics t ON t.id = p.topic_id
INNER JOIN categories c ON c.id = t.category_id
WHERE
t.archetype = 'regular' AND
p.user_id > 0 AND
cr.model_used = 'sentiment' AND
(p.created_at > :report_start AND p.created_at < :report_end)
GROUP BY DATE_TRUNC('day', p.created_at)
SQL
report_start: report.start_date,
report_end: report.end_date,
threshold: threshold,
)
data_points = %w[positive negative]
return report if grouped_sentiments.empty?
report.data =
data_points.map do |point|
{
req: "sentiment_#{point}",
color: point == "positive" ? report.colors[1] : report.colors[3],
label: I18n.t("discourse_ai.sentiment.reports.overall_sentiment.#{point}"),
data:
grouped_sentiments.map do |gs|
{ x: gs.posted_at, y: gs.public_send("#{point}_count") }
end,
}
end
end
plugin.add_report("post_emotion") do |report|
report.modes = [:radar]
threshold = 30
emotion_count_clause = Proc.new { |emotion| <<~SQL }
COUNT(
CASE WHEN (cr.classification::jsonb->'#{emotion}')::integer > :threshold THEN 1 ELSE NULL END
) AS #{emotion}_count
SQL
grouped_emotions =
DB.query(
<<~SQL,
SELECT
u.trust_level AS trust_level,
#{emotion_count_clause.call("sadness")},
#{emotion_count_clause.call("surprise")},
#{emotion_count_clause.call("fear")},
#{emotion_count_clause.call("anger")},
#{emotion_count_clause.call("joy")},
#{emotion_count_clause.call("disgust")}
FROM
classification_results AS cr
INNER JOIN posts p ON p.id = cr.target_id AND cr.target_type = 'Post'
INNER JOIN users u ON p.user_id = u.id
INNER JOIN topics t ON t.id = p.topic_id
INNER JOIN categories c ON c.id = t.category_id
WHERE
t.archetype = 'regular' AND
p.user_id > 0 AND
cr.model_used = 'emotion' AND
(p.created_at > :report_start AND p.created_at < :report_end)
GROUP BY u.trust_level
SQL
report_start: report.start_date,
report_end: report.end_date,
threshold: threshold,
)
emotions = %w[sadness disgust fear anger joy surprise]
level_groups = [[0, 1], [2, 3, 4]]
return report if grouped_emotions.empty?
report.data =
level_groups.each_with_index.map do |lg, idx|
tl_emotion_avgs = grouped_emotions.select { |ge| lg.include?(ge.trust_level) }
{
req: "emotion_tl_#{lg.join}",
color: report.colors[idx],
label: I18n.t("discourse_ai.sentiment.reports.post_emotion.tl_#{lg.join}"),
data:
emotions.map do |e|
{
x: I18n.t("discourse_ai.sentiment.reports.post_emotion.#{e}"),
y:
tl_emotion_avgs.sum do |tl_emotion_avg|
tl_emotion_avg.public_send("#{e}_count").to_i
end,
}
end,
}
end
end
end
end
end
end