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

Commit 65097b7

Browse files
committed
TABLE IS HAPPENING
1 parent f1fab47 commit 65097b7

File tree

13 files changed

+287
-211
lines changed

13 files changed

+287
-211
lines changed
Lines changed: 35 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,35 @@
1+
<div class="cell title">
2+
{{#if this.model.icon}}
3+
{{d-icon this.model.icon}}
4+
{{/if}}
5+
<a href="{{this.filterURL}}{{this.model.type}}">{{this.model.title}}</a>
6+
</div>
7+
8+
<div class="cell value today-count">{{number this.model.todayCount}}</div>
9+
10+
<div
11+
class="cell value yesterday-count {{this.model.yesterdayTrend}}"
12+
title={{this.model.yesterdayCountTitle}}
13+
>
14+
{{number this.model.yesterdayCount}}
15+
{{d-icon this.model.yesterdayTrendIcon}}
16+
</div>
17+
18+
<div
19+
class="cell value sevendays-count {{this.model.sevenDaysTrend}}"
20+
title={{this.model.sevenDaysCountTitle}}
21+
>
22+
{{number this.model.lastSevenDaysCount}}
23+
{{d-icon this.model.sevenDaysTrendIcon}}
24+
</div>
25+
26+
<div
27+
class="cell value thirty-days-count {{this.model.thirtyDaysTrend}}"
28+
title={{this.model.thirtyDaysCountTitle}}
29+
>
30+
{{number this.model.lastThirtyDaysCount}}
31+
32+
{{#if this.model.canDisplayTrendIcon}}
33+
{{d-icon this.model.thirtyDaysTrendIcon}}
34+
{{/if}}
35+
</div>
Lines changed: 12 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,12 @@
1+
import Component from "@ember/component";
2+
import { attributeBindings, classNames } from "@ember-decorators/component";
3+
import getURL from "discourse-common/lib/get-url";
4+
5+
@classNames("admin-report-counters")
6+
@attributeBindings("model.description:title")
7+
export default class AdminReportEmotion extends Component {
8+
get filterURL() {
9+
let aMonthAgo = moment().subtract(1, "month").format("YYYY-MM-DD");
10+
return getURL(`/filter?q=activity-after%3A${aMonthAgo}%20order%3A`);
11+
}
12+
}
Lines changed: 32 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -1,9 +1,37 @@
1-
import { computed } from "@ember/object";
21
import AdminDashboardTabController from "admin/controllers/admin-dashboard-tab";
32

43
export default class AdminDashboardSentiment extends AdminDashboardTabController {
5-
@computed("startDate", "endDate")
6-
get filters() {
7-
return { startDate: this.startDate, endDate: this.endDate };
4+
get emotions() {
5+
const emotions = [
6+
"admiration",
7+
"amusement",
8+
"anger",
9+
"annoyance",
10+
"approval",
11+
"caring",
12+
"confusion",
13+
"curiosity",
14+
"desire",
15+
"disappointment",
16+
"disapproval",
17+
"disgust",
18+
"embarrassment",
19+
"excitement",
20+
"fear",
21+
"gratitude",
22+
"grief",
23+
"joy",
24+
"love",
25+
"nervousness",
26+
"neutral",
27+
"optimism",
28+
"pride",
29+
"realization",
30+
"relief",
31+
"remorse",
32+
"sadness",
33+
"surprise",
34+
];
35+
return emotions;
836
}
937
}

assets/javascripts/discourse/templates/admin-dashboard-sentiment.hbs

Lines changed: 41 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -23,11 +23,46 @@
2323
@showHeader={{true}}
2424
/>
2525

26-
<AdminReport
27-
@dataSourceName="post_emotion"
28-
@filters={{this.filters}}
29-
@showHeader={{true}}
30-
/>
26+
27+
<div class="admin-report activity-metrics">
28+
<div class="header">
29+
<ul class="breadcrumb">
30+
<li class="item report">
31+
<LinkTo @route="adminReports" class="report-url">
32+
{{i18n "admin.dashboard.emotion"}}
33+
</LinkTo>
34+
</li>
35+
</ul>
36+
</div>
37+
<div class="report-body">
38+
<div class="counters-list">
39+
<div class="counters-header">
40+
<div class="counters-cell"></div>
41+
<div class="counters-cell">{{i18n
42+
"admin.dashboard.reports.today"
43+
}}</div>
44+
<div class="counters-cell">{{i18n
45+
"admin.dashboard.reports.yesterday"
46+
}}</div>
47+
<div class="counters-cell">{{i18n
48+
"admin.dashboard.reports.last_7_days"
49+
}}</div>
50+
<div class="counters-cell">{{i18n
51+
"admin.dashboard.reports.last_30_days"
52+
}}</div>
53+
</div>
54+
55+
{{#each this.emotions as |metric|}}
56+
<AdminReport
57+
@showHeader={{false}}
58+
@forcedModes="emotion"
59+
@dataSourceName="emotion_{{metric}}"
60+
/>
61+
{{/each}}
62+
</div>
63+
</div>
64+
</div>
65+
3166
</div>
3267
</div>
33-
</div>
68+
</div>

assets/stylesheets/modules/sentiment/common/dashboard.scss

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -4,5 +4,8 @@
44
grid-template-columns: repeat(12, 1fr);
55
grid-column-gap: 1em;
66
grid-row-gap: 1em;
7+
.admin-report {
8+
grid-column: span 12;
9+
}
710
}
811
}

assets/stylesheets/modules/sentiment/desktop/dashboard.scss

Lines changed: 0 additions & 8 deletions
This file was deleted.

assets/stylesheets/modules/sentiment/mobile/dashboard.scss

Lines changed: 0 additions & 10 deletions
This file was deleted.

config/locales/client.en.yml

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -11,6 +11,8 @@ en:
1111
site_settings:
1212
categories:
1313
discourse_ai: "Discourse AI"
14+
dashboard:
15+
emotion: "Emotion"
1416
js:
1517
discourse_automation:
1618
scriptables:

config/locales/server.en.yml

Lines changed: 59 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -79,13 +79,13 @@ en:
7979
ai_embeddings_semantic_related_include_closed_topics: "Include closed topics in semantic search results"
8080
ai_embeddings_semantic_search_hyde_model: "Model used to expand keywords to get better results during a semantic search"
8181
ai_embeddings_per_post_enabled: Generate embeddings for each post
82-
82+
8383
ai_summarization_enabled: "Enable the topic summarization module."
8484
ai_summarization_model: "Model to use for summarization."
8585
ai_custom_summarization_allowed_groups: "Groups allowed to use create new summaries."
8686
ai_pm_summarization_allowed_groups: "Groups allowed to create and view summaries in PMs."
8787
ai_summarize_max_hot_topics_gists_per_batch: "After updating topics in the hot list, we'll generate brief summaries of the first N ones. (Disabled when 0)"
88-
ai_hot_topic_gists_allowed_groups: "Groups allowed to see gists in the hot topics list."
88+
ai_hot_topic_gists_allowed_groups: "Groups allowed to see gists in the hot topics list."
8989
ai_summary_backfill_maximum_topics_per_hour: "Number of topic summaries to backfill per hour."
9090

9191
ai_bot_enabled: "Enable the AI Bot module."
@@ -111,14 +111,65 @@ en:
111111
reports:
112112
overall_sentiment:
113113
title: "Overall sentiment"
114-
description: "The chart compares the number of posts classified as either positive or negative. These are calculated when positive or negative scores > the set threshold score. This means neutral posts are not shown. Private messages (PMs) are also excluded. Classified with \"cardiffnlp/twitter-roberta-base-sentiment-latest\""
114+
description: 'The chart compares the number of posts classified as either positive or negative. These are calculated when positive or negative scores > the set threshold score. This means neutral posts are not shown. Private messages (PMs) are also excluded. Classified with "cardiffnlp/twitter-roberta-base-sentiment-latest"'
115115
xaxis: "Positive(%)"
116116
yaxis: "Date"
117-
post_emotion:
118-
title: "Post emotion"
119-
description: "Number of posts classified with one of the following emotions, grouped by poster's trust level. Posts that are not positive or negative and considered neutral, are not shown. Private messages (PMs) are also excluded. Classified with \"j-hartmann/emotion-english-roberta-large\""
120-
xaxis:
121-
yaxis:
117+
emotion_admiration:
118+
title: Admiration
119+
emotion_amusement:
120+
title: Amusement
121+
emotion_anger:
122+
title: Anger
123+
emotion_annoyance:
124+
title: Annoyance
125+
emotion_approval:
126+
title: Approval
127+
emotion_caring:
128+
title: Caring
129+
emotion_confusion:
130+
title: Confusion
131+
emotion_curiosity:
132+
title: Curiosity
133+
emotion_desire:
134+
title: Desire
135+
emotion_disappointment:
136+
title: Disappointment
137+
emotion_disapproval:
138+
title: Disapproval
139+
emotion_disgust:
140+
title: Disgust
141+
emotion_embarrassment:
142+
title: Embarrassment
143+
emotion_excitement:
144+
title: Excitement
145+
emotion_fear:
146+
title: Fear
147+
emotion_gratitude:
148+
title: Gratitude
149+
emotion_grief:
150+
title: Grief
151+
emotion_joy:
152+
title: Joy
153+
emotion_love:
154+
title: Love
155+
emotion_nervousness:
156+
title: Nervousness
157+
emotion_neutral:
158+
title: Neutral
159+
emotion_optimism:
160+
title: Optimism
161+
emotion_pride:
162+
title: Pride
163+
emotion_realization:
164+
title: Realization
165+
emotion_relief:
166+
title: Relief
167+
emotion_remorse:
168+
title: Remorse
169+
emotion_sadness:
170+
title: Sadness
171+
emotion_surprise:
172+
title: Surprise
122173

123174
discourse_ai:
124175
unknown_model: "Unknown AI model"

lib/sentiment/emotion_dashboard_report.rb

Lines changed: 40 additions & 57 deletions
Original file line numberDiff line numberDiff line change
@@ -3,64 +3,47 @@
33
module DiscourseAi
44
module Sentiment
55
class EmotionDashboardReport
6-
def self.report
7-
periods = {
8-
today: {
9-
start: 1.day.ago,
10-
end: Time.zone.now,
11-
},
12-
yesterday: {
13-
start: 2.days.ago,
14-
end: 1.day.ago,
15-
},
16-
yesterday_comparison: {
17-
start: 3.days.ago,
18-
end: 2.days.ago,
19-
},
20-
week: {
21-
start: 1.week.ago,
22-
end: Time.zone.now,
23-
},
24-
week_comparison: {
25-
start: 2.weeks.ago,
26-
end: 1.week.ago,
27-
},
28-
month: {
29-
start: 1.month.ago,
30-
end: Time.zone.now,
31-
},
32-
month_comparison: {
33-
start: 2.months.ago,
34-
end: 1.month.ago,
35-
},
36-
}
6+
def self.register!(plugin)
7+
Emotions::LIST.each do |emotion|
8+
plugin.add_report("emotion_#{emotion}") do |report|
9+
query_results = DiscourseAi::Sentiment::EmotionDashboardReport.fetch_data
10+
report.data = query_results.pop(30).map { |row| { x: row.day, y: row.send(emotion) } }
11+
report.prev30Days =
12+
query_results.take(30).map { |row| { x: row.day, y: row.send(emotion) } }
13+
end
14+
end
3715

38-
report = {}
39-
periods.each do |period, range|
40-
report[period] = DB.query(<<~SQL, start: range[:start], end: range[:end]).first.to_h
41-
SELECT
42-
#{
43-
DiscourseAi::Sentiment::Emotions::LIST
44-
.map do |emotion|
45-
"COUNT(*) FILTER (WHERE (classification_results.classification::jsonb->'#{emotion}')::float > 0.1) AS #{emotion}"
46-
end
47-
.join(",\n ")
48-
}
49-
FROM
50-
classification_results
51-
INNER JOIN
52-
posts ON posts.id = classification_results.target_id AND
53-
posts.deleted_at IS NULL AND
54-
posts.created_at >= :start AND
55-
posts.created_at < :end
56-
INNER JOIN
57-
topics ON topics.id = posts.topic_id AND
58-
topics.archetype = 'regular' AND
59-
topics.deleted_at IS NULL
60-
WHERE
61-
classification_results.target_type = 'Post' AND
62-
classification_results.model_used = 'SamLowe/roberta-base-go_emotions'
63-
SQL
16+
def self.fetch_data
17+
Discourse
18+
.cache
19+
.fetch("emotion_dashboard", expires_in: 5.minutes) do
20+
DB.query(<<~SQL, start: Time.now.midnight, end: 60.days.ago.midnight)
21+
SELECT
22+
posts.created_at::DATE + 540 AS day,
23+
#{
24+
DiscourseAi::Sentiment::Emotions::LIST
25+
.map do |emotion|
26+
"COUNT(*) FILTER (WHERE (classification_results.classification::jsonb->'#{emotion}')::float > 0.1) AS #{emotion}"
27+
end
28+
.join(",\n ")
29+
}
30+
FROM
31+
classification_results
32+
INNER JOIN
33+
posts ON posts.id = classification_results.target_id AND
34+
posts.deleted_at IS NULL AND
35+
posts.created_at BETWEEN :start AND :end
36+
INNER JOIN
37+
topics ON topics.id = posts.topic_id AND
38+
topics.archetype = 'regular' AND
39+
topics.deleted_at IS NULL
40+
WHERE
41+
classification_results.target_type = 'Post' AND
42+
classification_results.model_used = 'SamLowe/roberta-base-go_emotions'
43+
GROUP BY 1
44+
ORDER BY 1 ASC
45+
SQL
46+
end
6447
end
6548
end
6649
end

0 commit comments

Comments
 (0)