|
1 | 1 | # frozen_string_literal: true |
2 | | -# TODO: Currently returns all posts, need to add pagination! |
| 2 | + |
| 3 | +# TODO: Currently returns all posts, need to add pagination? |
3 | 4 |
|
4 | 5 | module DiscourseAi |
5 | 6 | module Sentiment |
6 | 7 | class SentimentAnalysisReport |
7 | 8 | def self.register!(plugin) |
8 | 9 | plugin.add_report("sentiment_analysis") do |report| |
9 | 10 | report.modes = [:sentiment_analysis] |
10 | | - |
11 | | - sentiment_data = |
12 | | - DB |
13 | | - .query(<<~SQL, report_start: report.start_date, report_end: report.end_date) |
14 | | -WITH topic_tags_cte AS ( |
15 | | - SELECT |
16 | | - tt.topic_id, |
17 | | - string_agg(DISTINCT tags.name, ',') AS tag_names |
18 | | - FROM topic_tags tt |
19 | | - JOIN tags ON tags.id = tt.tag_id |
20 | | - GROUP BY tt.topic_id |
21 | | -) |
22 | | -SELECT |
23 | | - t.id AS topic_id, |
24 | | - t.title, |
25 | | - p.id AS post_id, |
26 | | - p.post_number, |
27 | | - u.username, |
28 | | - LEFT(p.cooked, 300) AS post_excerpt, |
29 | | - c.id AS category_id, |
30 | | - c.name AS category_name, |
31 | | - COALESCE(tt.tag_names, '') AS tag_names, |
32 | | - (cr.classification::jsonb->'positive')::float AS positive_score, |
33 | | - (cr.classification::jsonb->'negative')::float AS negative_score |
34 | | -FROM classification_results cr |
35 | | -JOIN posts p |
36 | | - ON p.id = cr.target_id |
37 | | - AND cr.target_type = 'Post' |
38 | | -JOIN topics t |
39 | | - ON t.id = p.topic_id |
40 | | -JOIN categories c |
41 | | - ON c.id = t.category_id |
42 | | -JOIN users u |
43 | | - ON u.id = p.user_id |
44 | | -LEFT JOIN topic_tags_cte tt |
45 | | - ON tt.topic_id = t.id |
46 | | -WHERE |
47 | | - (p.created_at > :report_start AND p.created_at < :report_end) |
48 | | - AND cr.model_used = 'cardiffnlp/twitter-roberta-base-sentiment-latest' |
49 | | - AND p.deleted_at IS NULL |
50 | | - AND p.hidden = FALSE |
51 | | - AND t.deleted_at IS NULL |
52 | | - AND t.visible = TRUE |
53 | | - AND t.archetype != 'private_message' |
54 | | - AND c.read_restricted = FALSE |
55 | | -ORDER BY c.name, (cr.classification::jsonb->'negative')::float DESC |
56 | | -SQL |
57 | | - .map do |row| |
58 | | - # Add neutral score and structure data |
59 | | - positive_score = row.positive_score || 0.0 |
60 | | - negative_score = row.negative_score || 0.0 |
61 | | - neutral_score = 1.0 - (positive_score + negative_score) |
62 | | - |
63 | | - { |
64 | | - category_name: row.category_name, |
65 | | - topic_id: row.topic_id, |
66 | | - title: row.title, |
67 | | - post_id: row.post_id, |
68 | | - post_number: row.post_number, |
69 | | - username: row.username, |
70 | | - post_excerpt: row.post_excerpt, |
71 | | - category_id: row.category_id, |
72 | | - tag_names: row.tag_names, |
73 | | - positive_score: positive_score, |
74 | | - negative_score: negative_score, |
75 | | - neutral_score: neutral_score, |
76 | | - } |
77 | | - end |
| 11 | + sentiment_data = DiscourseAi::Sentiment::SentimentAnalysisReport.fetch_data(report) |
78 | 12 |
|
79 | 13 | # Group posts by category |
80 | 14 | # ! TODO: by tags? |
@@ -114,6 +48,77 @@ def self.register!(plugin) |
114 | 48 | end |
115 | 49 | end |
116 | 50 | end |
| 51 | + |
| 52 | + def self.fetch_data(report) |
| 53 | + DB |
| 54 | + .query(<<~SQL, report_start: report.start_date, report_end: report.end_date) |
| 55 | + WITH topic_tags_cte AS ( |
| 56 | + SELECT |
| 57 | + tt.topic_id, |
| 58 | + string_agg(DISTINCT tags.name, ',') AS tag_names |
| 59 | + FROM topic_tags tt |
| 60 | + JOIN tags ON tags.id = tt.tag_id |
| 61 | + GROUP BY tt.topic_id |
| 62 | + ) |
| 63 | + SELECT |
| 64 | + t.id AS topic_id, |
| 65 | + t.title, |
| 66 | + p.id AS post_id, |
| 67 | + p.post_number, |
| 68 | + u.username, |
| 69 | + LEFT(p.cooked, 300) AS post_excerpt, |
| 70 | + c.id AS category_id, |
| 71 | + c.name AS category_name, |
| 72 | + COALESCE(tt.tag_names, '') AS tag_names, |
| 73 | + (cr.classification::jsonb->'positive')::float AS positive_score, |
| 74 | + (cr.classification::jsonb->'negative')::float AS negative_score |
| 75 | + FROM classification_results cr |
| 76 | + JOIN posts p |
| 77 | + ON p.id = cr.target_id |
| 78 | + AND cr.target_type = 'Post' |
| 79 | + JOIN topics t |
| 80 | + ON t.id = p.topic_id |
| 81 | + JOIN categories c |
| 82 | + ON c.id = t.category_id |
| 83 | + JOIN users u |
| 84 | + ON u.id = p.user_id |
| 85 | + LEFT JOIN topic_tags_cte tt |
| 86 | + ON tt.topic_id = t.id |
| 87 | + WHERE |
| 88 | + p.created_at BETWEEN :report_start AND :report_end |
| 89 | + AND cr.model_used = 'cardiffnlp/twitter-roberta-base-sentiment-latest' |
| 90 | + AND p.deleted_at IS NULL |
| 91 | + AND p.hidden = FALSE |
| 92 | + AND t.deleted_at IS NULL |
| 93 | + AND t.visible = TRUE |
| 94 | + AND t.archetype != 'private_message' |
| 95 | + AND c.read_restricted = FALSE |
| 96 | + ORDER BY |
| 97 | + c.name, |
| 98 | + (cr.classification::jsonb->'negative')::float DESC |
| 99 | + SQL |
| 100 | + .map do |row| |
| 101 | + # Add neutral score and structure data |
| 102 | + positive_score = row.positive_score || 0.0 |
| 103 | + negative_score = row.negative_score || 0.0 |
| 104 | + neutral_score = 1.0 - (positive_score + negative_score) |
| 105 | + |
| 106 | + { |
| 107 | + category_name: row.category_name, |
| 108 | + topic_id: row.topic_id, |
| 109 | + title: row.title, |
| 110 | + post_id: row.post_id, |
| 111 | + post_number: row.post_number, |
| 112 | + username: row.username, |
| 113 | + post_excerpt: row.post_excerpt, |
| 114 | + category_id: row.category_id, |
| 115 | + tag_names: row.tag_names, |
| 116 | + positive_score: positive_score, |
| 117 | + negative_score: negative_score, |
| 118 | + neutral_score: neutral_score, |
| 119 | + } |
| 120 | + end |
| 121 | + end |
117 | 122 | end |
118 | 123 | end |
119 | 124 | end |
0 commit comments