Skip to content
This repository was archived by the owner on Jul 22, 2025. It is now read-only.
Closed
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
Original file line number Diff line number Diff line change
Expand Up @@ -37,7 +37,7 @@ def posts
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,
Expand Down
4 changes: 0 additions & 4 deletions app/models/classification_result.rb
Original file line number Diff line number Diff line change
Expand Up @@ -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
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -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);
Expand Down Expand Up @@ -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"];
}
Expand Down Expand Up @@ -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;
});
}
Expand Down Expand Up @@ -171,57 +186,13 @@ 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) {
// Don't do anything if the same chart is clicked again
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;

Expand All @@ -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);
}
Expand All @@ -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|}}
Expand All @@ -310,7 +252,6 @@ export default class AdminReportSentimentAnalysis extends Component {
@data={{data.scores}}
@totalScore={{data.total_score}}
@doughnutTitle={{data.title}}
@displayLegend={{true}}
/>
</div>
{{/each}}
Expand All @@ -319,33 +260,20 @@ 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}}
@colors={{this.colors}}
@data={{this.selectedChart.scores}}
@totalScore={{this.selectedChart.total_score}}
@doughnutTitle={{this.selectedChart.title}}
@displayLegend={{true}}
/>

</div>
<div class="admin-report-sentiment-analysis-details">
<HorizontalOverflowNav
Expand Down
9 changes: 1 addition & 8 deletions assets/javascripts/discourse/components/doughnut-chart.gjs
Original file line number Diff line number Diff line change
@@ -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 || "";

Expand All @@ -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",
},
},
},
Expand Down
7 changes: 0 additions & 7 deletions assets/javascripts/initializers/admin-reports.js
Original file line number Diff line number Diff line change
Expand Up @@ -22,13 +22,6 @@ export default {
"sentiment_analysis",
AdminReportSentimentAnalysis
);

api.registerValueTransformer(
"admin-reports-show-query-params",
({ value }) => {
return [...value, "selectedChart"];
}
);
});
},
};
36 changes: 15 additions & 21 deletions assets/javascripts/initializers/ai-sentiment-admin-nav.js
Original file line number Diff line number Diff line change
@@ -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",
});
});
Loading