136 lines
4.7 KiB
Ruby
136 lines
4.7 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[:lime] : report.colors[:purple],
|
|
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 = [:stacked_line_chart]
|
|
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
|
|
DATE_TRUNC('day', p.created_at)::DATE AS posted_at,
|
|
#{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 DATE_TRUNC('day', p.created_at)
|
|
SQL
|
|
report_start: report.start_date,
|
|
report_end: report.end_date,
|
|
threshold: threshold,
|
|
)
|
|
|
|
return report if grouped_emotions.empty?
|
|
|
|
emotions = [
|
|
{ name: "sadness", color: report.colors[:turquoise] },
|
|
{ name: "disgust", color: report.colors[:lime] },
|
|
{ name: "fear", color: report.colors[:purple] },
|
|
{ name: "anger", color: report.colors[:magenta] },
|
|
{ name: "joy", color: report.colors[:yellow] },
|
|
{ name: "surprise", color: report.colors[:brown] },
|
|
]
|
|
|
|
report.data =
|
|
emotions.map do |emotion|
|
|
{
|
|
req: "emotion_#{emotion[:name]}",
|
|
color: emotion[:color],
|
|
label: I18n.t("discourse_ai.sentiment.reports.post_emotion.#{emotion[:name]}"),
|
|
data:
|
|
grouped_emotions.map do |ge|
|
|
{ x: ge.posted_at, y: ge.public_send("#{emotion[:name]}_count") }
|
|
end,
|
|
}
|
|
end
|
|
end
|
|
end
|
|
end
|
|
end
|
|
end
|