Skip to content

Commit fcc6a81

Browse files
goapunkhom3mad3
authored andcommitted
comments_async: redesign ai report
1 parent e10457d commit fcc6a81

File tree

5 files changed

+109
-35
lines changed

5 files changed

+109
-35
lines changed

adhocracy4/comments_async/static/comments_async/__tests__/AiReport.jest.jsx

+40-22
Original file line numberDiff line numberDiff line change
@@ -3,27 +3,45 @@ import { render, fireEvent, screen } from '@testing-library/react'
33
import '@testing-library/jest-dom'
44
import AiReport from '../ai_report'
55

6-
test('Test render <AiReport> with Read More button', () => {
7-
render(
8-
<AiReport
9-
Report={{ explanation: 'This is the ai report', show_in_discussion: true }}
10-
/>
11-
)
12-
const comment = screen.getByText('Read more')
13-
expect(comment).toBeTruthy()
14-
})
6+
describe('Test AiReport', () => {
7+
test('renders with Read More button', () => {
8+
render(
9+
<AiReport
10+
report={{ label: [['cattest', 'test label']], confidence: [['cattest', 0.65]], explanation: { cattest: [['word', 0.61]] }, show_in_discussion: true }}
11+
/>
12+
)
13+
const comment = screen.getByText('Read more')
14+
expect(comment).toBeInTheDocument()
15+
})
16+
17+
test('functionality of Read More button', () => {
18+
render(
19+
<AiReport
20+
report={{ label: [['cattest', 'test label']], confidence: [['cattest', 0.65]], explanation: { cattest: [['word', 0.61]] }, show_in_discussion: true }}
21+
/>
22+
)
23+
const readMore = screen.getByText('Read more')
24+
expect(readMore).toBeInTheDocument()
25+
const button = screen.getByRole('button')
26+
expect(button).toBeInTheDocument()
27+
fireEvent.click(button)
28+
const readLess = screen.getByText('Show less')
29+
expect(readLess).toBeInTheDocument()
30+
expect(readLess).toBeInTheDocument()
31+
})
1532

16-
test('Test functionality of Read More <AiReport>', () => {
17-
render(
18-
<AiReport
19-
Report={{ explanation: 'This is the ai report', show_in_discussion: true }}
20-
/>
21-
)
22-
const readMore = screen.getByText('Read more')
23-
expect(readMore).toBeTruthy()
24-
const button = screen.getByRole('button')
25-
expect(button).toBeTruthy()
26-
fireEvent.click(button)
27-
const readLess = screen.getByText('Show less')
28-
expect(readLess).toBeTruthy()
33+
test('shows percentage for each label', async () => {
34+
render(
35+
<AiReport
36+
report={{ label: [['cattest', 'test label']], confidence: [0.65], explanation: { cattest: [['word', 0.61]] }, show_in_discussion: true }}
37+
/>
38+
)
39+
const readMore = screen.getByText('Read more')
40+
expect(readMore).toBeInTheDocument()
41+
const button = screen.getByRole('button')
42+
expect(button).toBeInTheDocument()
43+
fireEvent.click(button)
44+
const percent = screen.getByText(/65%/)
45+
expect(percent).toBeInTheDocument()
46+
})
2947
})

adhocracy4/comments_async/static/comments_async/ai_report.jsx

+57-11
Original file line numberDiff line numberDiff line change
@@ -3,9 +3,10 @@ import django from 'django'
33
import { SwitchButton } from '../../../static/SwitchButton'
44

55
const translated = {
6-
intro: django.pgettext('defakts', 'This comment contains disinformation. Defakts uses an Artificial ' +
7-
'Intelligence to scan content for disinformation. Disinformation often shows ' +
8-
'certain characteristics that allow for a reliable identification.'),
6+
expandableBar: django.pgettext('defakts', 'The defakt AI has found evidence of disinformation.'),
7+
intro: django.pgettext('defakts', 'Defakts uses artificial intelligence to check content for disinformation based on certain linguistic characteristics.'),
8+
confidenceScore: django.pgettext('defakts', 'The AI considers some of the characteristics of disinformation to be fulfilled based on the following words in the comment. The probability is given in % for each characteristic.'),
9+
cta: django.pgettext('defakts', 'If you want to know more about what the characteristics mean, please visit our [FAQs]. Here you will also find advice on how to respond constructively to disinformation.'),
910
ariaReadMore: django.pgettext('defakts', 'Click to view the AI explanation for reporting this comment.'),
1011
ariaReadLess: django.pgettext('defakts', 'Click to hide the AI explanation for reporting this comment.'),
1112
readMore: django.pgettext('defakts', 'Read more'),
@@ -14,7 +15,7 @@ const translated = {
1415
hideInfoSwitch: django.pgettext('defakts', 'Hide AI info from users')
1516
}
1617

17-
export const AiReport = ({ Report, notificationPk, toggleShowAiReport }) => {
18+
export const AiReport = ({ report, notificationPk, toggleShowAiReport }) => {
1819
const [isExpanded, setIsExpanded] = useState()
1920

2021
const toggleExpand = () => {
@@ -32,31 +33,76 @@ export const AiReport = ({ Report, notificationPk, toggleShowAiReport }) => {
3233
</button>
3334
)
3435

36+
const confidenceToPercent = (confidence) => {
37+
const percentFormat = new Intl.NumberFormat('default', {
38+
style: 'percent',
39+
minimumFractionDigits: 0,
40+
maximumFractionDigits: 0
41+
})
42+
return percentFormat.format(confidence)
43+
}
44+
45+
const extractLabelWords = (label) => {
46+
const words = report.explanation[label]
47+
return words.slice(0, 3).map(word => word[0]).join(', ')
48+
}
49+
50+
const renderExplanation = (
51+
<ul>
52+
{report.label.map(([key, description], index) => (
53+
<li key={key}>
54+
<span>{description.charAt(0).toUpperCase() + description.slice(1)} </span>
55+
<span>({confidenceToPercent(report.confidence[index])}): </span>
56+
<span>{extractLabelWords(key)}</span>
57+
</li>
58+
))}
59+
</ul>
60+
)
61+
62+
const renderCTA = () => {
63+
// replace "[FAQs]" with anchor
64+
const [preText, placeholderText, postText] = translated.cta.split(/(\[.*?\])/)
65+
// remove square brackets
66+
const anchorText = placeholderText.replace(/\[|\]/g, '')
67+
return (
68+
<>
69+
{preText} <a href={report.faq_url} target="_blank" rel="noreferrer">{anchorText}</a> {postText}
70+
</>
71+
)
72+
}
73+
3574
return (
36-
<div className="alert alert--danger mb-4">
37-
<div className="d-flex text-start mb-4">
75+
<div className="alert alert--danger mb-2 pb-0">
76+
<div className="d-flex text-start">
3877
<i
3978
className="fas fa-exclamation-circle text-danger pt-1 pe-2"
4079
aria-hidden="True"
4180
/>
4281

4382
{!isExpanded
4483
? (
45-
<p className="pe-4">{translated.intro} {toggleReadMore}</p>
84+
<p className="pe-4">{translated.expandableBar} {toggleReadMore}</p>
4685
)
4786
: (
4887
<div className="pe-4">
49-
<p>{translated.intro}</p>
50-
<p>{Report.explanation} {toggleReadMore}</p>
88+
<p>{translated.expandableBar}</p>
89+
<p>
90+
<span>{translated.intro} </span>
91+
<span>{translated.confidenceScore}</span>
92+
</p>
93+
{renderExplanation}
94+
<p>
95+
{renderCTA()} {toggleReadMore}
96+
</p>
5197
</div>
5298
)}
5399
</div>
54100
{toggleShowAiReport &&
55-
<div className="d-flex text-start">
101+
<div className="d-flex text-start mt-3 mb-3">
56102
<SwitchButton
57103
id={notificationPk}
58104
onClickCallback={toggleShowAiReport}
59-
isChecked={Report.show_in_discussion}
105+
isChecked={report.show_in_discussion}
60106
switchLabelOn={translated.hideInfoSwitch}
61107
switchLabelOff={translated.showInfoSwitch}
62108
/>

adhocracy4/comments_async/static/comments_async/comment.jsx

+1-1
Original file line numberDiff line numberDiff line change
@@ -370,7 +370,7 @@ export default class Comment extends React.Component {
370370

371371
{this.props.aiReport && this.props.aiReport.show_in_discussion &&
372372
<AiReport
373-
Report={this.props.aiReport}
373+
report={this.props.aiReport}
374374
/>}
375375

376376
{this.state.showModStatement && this.state.moderatorFeedback &&

adhocracy4/comments_async/static/comments_async/comment_list.jsx

+6-1
Original file line numberDiff line numberDiff line change
@@ -7,6 +7,11 @@ const CommentList = (props) => {
77
<ul className="u-list-reset a4-comments">
88
{
99
props.comments.map((comment, index) => {
10+
// don't show ai report if there are no labels
11+
let aiReport = null
12+
if (comment.ai_report && comment.ai_report.label.length > 0) {
13+
aiReport = comment.ai_report
14+
}
1015
return (
1116
<Comment
1217
comment_categories={comment.comment_categories}
@@ -60,7 +65,7 @@ const CommentList = (props) => {
6065
wouldHaveCommentingPermission={props.wouldHaveCommentingPermission}
6166
projectIsPublic={props.projectIsPublic}
6267
moderatorFeedback={comment.moderator_feedback ? comment.moderator_feedback : null}
63-
aiReport={comment.ai_report ? comment.ai_report : null}
68+
aiReport={aiReport}
6469
useTermsOfUse={props.useTermsOfUse}
6570
agreedTermsOfUse={props.agreedTermsOfUse}
6671
orgTermsUrl={props.orgTermsUrl}

changelog/8372.md

+5
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,5 @@
1+
### Changed
2+
3+
- redesign AI report to be readable
4+
- only show AI report when there's a useful label (catnodecis and catneutral don't
5+
count as labels)

0 commit comments

Comments
 (0)