Revert "DEV: Updates to sentiment analysis reports (#1161)"

This reverts commit 8863cf0c86140065978d33ee9dca421fc8670fb5.

The conditionals added around the report registrations are incompatible with multisite environment and skip_db=true
This commit is contained in:
David Taylor 2025-03-06 00:18:38 +00:00
parent e255c7a8f0
commit 86bdc7b517
No known key found for this signature in database
GPG Key ID: 46904C18B1D3F434
9 changed files with 75 additions and 201 deletions

View File

@ -37,7 +37,7 @@ module DiscourseAi
SELECT
p.id AS post_id,
p.topic_id,
t.fancy_title AS topic_title,
t.title AS topic_title,
p.cooked as post_cooked,
p.user_id,
p.post_number,

View File

@ -2,10 +2,6 @@
class ClassificationResult < ActiveRecord::Base
belongs_to :target, polymorphic: true
def self.has_sentiment_classification?
where(classification_type: "sentiment").exists?
end
end
# == Schema Information

View File

@ -3,36 +3,26 @@ import { tracked } from "@glimmer/tracking";
import { fn, hash } from "@ember/helper";
import { on } from "@ember/modifier";
import { action } from "@ember/object";
import didInsert from "@ember/render-modifiers/modifiers/did-insert";
import { service } from "@ember/service";
import { modifier } from "ember-modifier";
import { and } from "truth-helpers";
import DButton from "discourse/components/d-button";
import HorizontalOverflowNav from "discourse/components/horizontal-overflow-nav";
import PostList from "discourse/components/post-list";
import dIcon from "discourse/helpers/d-icon";
import replaceEmoji from "discourse/helpers/replace-emoji";
import { ajax } from "discourse/lib/ajax";
import { popupAjaxError } from "discourse/lib/ajax-error";
import { getAbsoluteURL } from "discourse/lib/get-url";
import discourseLater from "discourse/lib/later";
import { clipboardCopy } from "discourse/lib/utilities";
import Post from "discourse/models/post";
import closeOnClickOutside from "discourse/modifiers/close-on-click-outside";
import { i18n } from "discourse-i18n";
import DTooltip from "float-kit/components/d-tooltip";
import DoughnutChart from "discourse/plugins/discourse-ai/discourse/components/doughnut-chart";
export default class AdminReportSentimentAnalysis extends Component {
@service router;
@tracked selectedChart = null;
@tracked posts = [];
@tracked posts = null;
@tracked hasMorePosts = false;
@tracked nextOffset = 0;
@tracked showingSelectedChart = false;
@tracked activeFilter = "all";
@tracked shareIcon = "link";
setActiveFilter = modifier((element) => {
this.clearActiveFilters(element);
@ -81,6 +71,32 @@ export default class AdminReportSentimentAnalysis extends Component {
}
}
doughnutTitle(data) {
const MAX_TITLE_LENGTH = 18;
const title = data?.title || "";
const score = data?.total_score ? ` (${data.total_score})` : "";
if (title.length + score.length > MAX_TITLE_LENGTH) {
return (
title.substring(0, MAX_TITLE_LENGTH - score.length) + "..." + score
);
}
return title + score;
}
async postRequest() {
return await ajax("/discourse-ai/sentiment/posts", {
data: {
group_by: this.currentGroupFilter,
group_value: this.selectedChart?.title,
start_date: this.args.model.start_date,
end_date: this.args.model.end_date,
offset: this.nextOffset,
},
});
}
get colors() {
return ["#2ecc71", "#95a5a6", "#e74c3c"];
}
@ -117,11 +133,10 @@ export default class AdminReportSentimentAnalysis extends Component {
}
return this.posts.filter((post) => {
post.topic_title = replaceEmoji(post.topic_title);
if (this.activeFilter === "all") {
return true;
}
return post.sentiment === this.activeFilter;
});
}
@ -171,42 +186,6 @@ export default class AdminReportSentimentAnalysis extends Component {
];
}
async postRequest() {
return await ajax("/discourse-ai/sentiment/posts", {
data: {
group_by: this.currentGroupFilter,
group_value: this.selectedChart?.title,
start_date: this.args.model.start_date,
end_date: this.args.model.end_date,
offset: this.nextOffset,
},
});
}
@action
async openToChart() {
const queryParams = this.router.currentRoute.queryParams;
if (queryParams.selectedChart) {
this.selectedChart = this.transformedData.find(
(data) => data.title === queryParams.selectedChart
);
if (!this.selectedChart) {
return;
}
this.showingSelectedChart = true;
try {
const response = await this.postRequest();
this.posts = response.posts.map((post) => Post.create(post));
this.hasMorePosts = response.has_more;
this.nextOffset = response.next_offset;
} catch (e) {
popupAjaxError(e);
}
}
}
@action
async showDetails(data) {
if (this.selectedChart === data) {
@ -214,14 +193,6 @@ export default class AdminReportSentimentAnalysis extends Component {
return;
}
const currentQueryParams = this.router.currentRoute.queryParams;
this.router.transitionTo(this.router.currentRoute.name, {
queryParams: {
...currentQueryParams,
selectedChart: data.title,
},
});
this.selectedChart = data;
this.showingSelectedChart = true;
@ -246,10 +217,7 @@ export default class AdminReportSentimentAnalysis extends Component {
this.hasMorePosts = response.has_more;
this.nextOffset = response.next_offset;
const mappedPosts = response.posts.map((post) => Post.create(post));
this.posts.pushObjects(mappedPosts);
return mappedPosts;
return response.posts.map((post) => Post.create(post));
} catch (e) {
popupAjaxError(e);
}
@ -260,35 +228,9 @@ export default class AdminReportSentimentAnalysis extends Component {
this.showingSelectedChart = false;
this.selectedChart = null;
this.activeFilter = "all";
this.posts = [];
const currentQueryParams = this.router.currentRoute.queryParams;
this.router.transitionTo(this.router.currentRoute.name, {
queryParams: {
...currentQueryParams,
selectedChart: null,
},
});
}
@action
shareChart() {
const url = this.router.currentURL;
if (!url) {
return;
}
clipboardCopy(getAbsoluteURL(url));
this.shareIcon = "check";
discourseLater(() => {
this.shareIcon = "link";
}, 2000);
}
<template>
<span {{didInsert this.openToChart}}></span>
{{#unless this.showingSelectedChart}}
<div class="admin-report-sentiment-analysis">
{{#each this.transformedData as |data|}}
@ -310,7 +252,6 @@ export default class AdminReportSentimentAnalysis extends Component {
@data={{data.scores}}
@totalScore={{data.total_score}}
@doughnutTitle={{data.title}}
@displayLegend={{true}}
/>
</div>
{{/each}}
@ -319,23 +260,12 @@ export default class AdminReportSentimentAnalysis extends Component {
{{#if (and this.selectedChart this.showingSelectedChart)}}
<div class="admin-report-sentiment-analysis__selected-chart">
<div class="admin-report-sentiment-analysis__selected-chart-actions">
<DButton
@label="back_button"
@icon="chevron-left"
class="btn-flat"
@action={{this.backToAllCharts}}
/>
<DTooltip
class="share btn-flat"
@icon={{this.shareIcon}}
{{on "click" this.shareChart}}
@content={{i18n
"discourse_ai.sentiments.sentiment_analysis.share_chart"
}}
/>
</div>
<DButton
@label="back_button"
@icon="chevron-left"
class="btn-flat"
@action={{this.backToAllCharts}}
/>
<DoughnutChart
@labels={{@model.labels}}
@ -343,9 +273,7 @@ export default class AdminReportSentimentAnalysis extends Component {
@data={{this.selectedChart.scores}}
@totalScore={{this.selectedChart.total_score}}
@doughnutTitle={{this.selectedChart.title}}
@displayLegend={{true}}
/>
</div>
<div class="admin-report-sentiment-analysis-details">
<HorizontalOverflowNav

View File

@ -1,10 +1,7 @@
import Component from "@glimmer/component";
import { tracked } from "@glimmer/tracking";
import Chart from "admin/components/chart";
export default class DoughnutChart extends Component {
@tracked canvasSize = null;
get config() {
const totalScore = this.args.totalScore || "";
@ -16,18 +13,14 @@ export default class DoughnutChart extends Component {
{
data: this.args.data,
backgroundColor: this.args.colors,
cutout: "50%",
radius: 100,
},
],
},
options: {
responsive: true,
maintainAspectRatio: false,
plugins: {
legend: {
display: this.args.displayLegend || false,
position: "bottom",
position: this.args.legendPosition || "bottom",
},
},
},

View File

@ -22,13 +22,6 @@ export default {
"sentiment_analysis",
AdminReportSentimentAnalysis
);
api.registerValueTransformer(
"admin-reports-show-query-params",
({ value }) => {
return [...value, "selectedChart"];
}
);
});
},
};

View File

@ -1,27 +1,21 @@
import { apiInitializer } from "discourse/lib/api";
export default apiInitializer("1.15.0", (api) => {
const currentUser = api.getCurrentUser();
const settings = api.container.lookup("service:site-settings");
if (
!currentUser ||
!currentUser.admin ||
!currentUser.can_see_sentiment_reports
) {
return;
if (settings.ai_sentiment_enabled) {
api.addAdminSidebarSectionLink("reports", {
name: "sentiment_overview",
route: "admin.dashboardSentiment",
label: "discourse_ai.sentiments.sidebar.overview",
icon: "chart-column",
});
api.addAdminSidebarSectionLink("reports", {
name: "sentiment_analysis",
route: "adminReports.show",
routeModels: ["sentiment_analysis"],
label: "discourse_ai.sentiments.sidebar.analysis",
icon: "chart-pie",
});
}
api.addAdminSidebarSectionLink("reports", {
name: "sentiment_overview",
route: "admin.dashboardSentiment",
label: "discourse_ai.sentiments.sidebar.overview",
icon: "chart-column",
});
api.addAdminSidebarSectionLink("reports", {
name: "sentiment_analysis",
route: "adminReports.show",
routeModels: ["sentiment_analysis"],
label: "discourse_ai.sentiments.sidebar.analysis",
icon: "chart-pie",
});
});

View File

@ -39,12 +39,13 @@
flex: 1;
}
// Hides tag selector when showing subcategories selector
.control:nth-of-type(6):nth-last-of-type(3) {
display: none;
.control:nth-of-type(n + 6) {
flex-basis: 49%;
align-self: flex-end;
}
.control:has(.export-csv-btn) {
// Hides tag selector when showing subcategories selector
.control:nth-of-type(6):nth-last-of-type(3) {
display: none;
}
}
@ -54,6 +55,7 @@
display: flex;
flex-flow: row wrap;
order: 2;
gap: 1rem;
align-items: flex-start;
max-height: 100vh;
}
@ -63,31 +65,30 @@
@include report-container-box();
flex: 2;
display: flex;
gap: 1rem;
justify-content: space-around;
align-items: center;
flex-flow: row wrap;
gap: 3rem;
.admin-report-doughnut {
max-width: 300px;
max-height: 300px;
padding: 0.25rem;
}
&__chart-wrapper {
height: fit-content;
position: relative;
transition: transform 0.25s ease, box-shadow 0.25s ease;
border-radius: var(--d-border-radius);
width: auto;
.doughnut-chart-title {
@include ellipsis;
margin: 0 auto;
margin-top: 1rem;
text-align: center;
margin-bottom: 1rem;
max-width: 300px;
}
transition: transform 0.25s ease, box-shadow 0.25s ease;
border-radius: var(--d-border-radius);
&:hover {
box-shadow: var(--shadow-card);
transform: translateY(-1rem);
box-shadow: var(--shadow-card);
cursor: pointer;
}
}
@ -101,23 +102,11 @@
font-size: var(--font-up-2);
margin: 0 auto;
text-align: center;
margin-bottom: 1rem;
margin-top: 0.3rem;
padding-top: 2rem;
}
}
&__selected-chart-actions {
display: flex;
align-items: center;
padding-bottom: 0.35rem;
border-bottom: 1px solid var(--primary-low);
.share {
margin-left: auto;
.d-icon-check {
color: var(--success);
}
padding-bottom: 1rem;
border-top: 1px solid var(--primary-low);
}
}
}
@ -132,12 +121,11 @@
@include report-container-box();
flex: 1 1 300px;
min-width: 300px;
margin-left: 1rem;
display: flex;
flex-flow: column nowrap;
overflow-y: auto;
height: 100%;
padding-top: 0;
&__filters {
border-bottom: 1px solid var(--primary-low);
@ -212,13 +200,3 @@
}
}
}
.admin-reports.admin-contents .sentiment-analysis {
.horizontal-overflow-nav {
background: var(--secondary);
position: sticky;
top: 0;
padding-top: 1rem;
z-index: z("header");
}
}

View File

@ -678,7 +678,6 @@ en:
overview: "Sentiment overview"
analysis: "Sentiment analysis"
sentiment_analysis:
share_chart: "Copy link to chart"
filter_types:
all: "All"
positive: "Positive"

View File

@ -14,17 +14,10 @@ module DiscourseAi
plugin.on(:post_created, &sentiment_analysis_cb)
plugin.on(:post_edited, &sentiment_analysis_cb)
plugin.add_to_serializer(:current_user, :can_see_sentiment_reports) do
ClassificationResult.has_sentiment_classification? && SiteSetting.ai_sentiment_enabled
end
if Rails.env.test? ||
ClassificationResult.has_sentiment_classification? && SiteSetting.ai_sentiment_enabled
EmotionFilterOrder.register!(plugin)
EmotionDashboardReport.register!(plugin)
SentimentDashboardReport.register!(plugin)
SentimentAnalysisReport.register!(plugin)
end
EmotionFilterOrder.register!(plugin)
EmotionDashboardReport.register!(plugin)
SentimentDashboardReport.register!(plugin)
SentimentAnalysisReport.register!(plugin)
end
end
end