diff --git a/adhocracy4/comments_async/static/comments_async/__tests__/AiReport.jest.jsx b/adhocracy4/comments_async/static/comments_async/__tests__/AiReport.jest.jsx index 58ba7a29e..059aeffc5 100644 --- a/adhocracy4/comments_async/static/comments_async/__tests__/AiReport.jest.jsx +++ b/adhocracy4/comments_async/static/comments_async/__tests__/AiReport.jest.jsx @@ -3,27 +3,45 @@ import { render, fireEvent, screen } from '@testing-library/react' import '@testing-library/jest-dom' import AiReport from '../ai_report' -test('Test render with Read More button', () => { - render( - - ) - const comment = screen.getByText('Read more') - expect(comment).toBeTruthy() -}) +describe('Test AiReport', () => { + test('renders with Read More button', () => { + render( + + ) + const comment = screen.getByText('Read more') + expect(comment).toBeInTheDocument() + }) + + test('functionality of Read More button', () => { + render( + + ) + const readMore = screen.getByText('Read more') + expect(readMore).toBeInTheDocument() + const button = screen.getByRole('button') + expect(button).toBeInTheDocument() + fireEvent.click(button) + const readLess = screen.getByText('Show less') + expect(readLess).toBeInTheDocument() + expect(readLess).toBeInTheDocument() + }) -test('Test functionality of Read More ', () => { - render( - - ) - const readMore = screen.getByText('Read more') - expect(readMore).toBeTruthy() - const button = screen.getByRole('button') - expect(button).toBeTruthy() - fireEvent.click(button) - const readLess = screen.getByText('Show less') - expect(readLess).toBeTruthy() + test('shows percentage for each label', async () => { + render( + + ) + const readMore = screen.getByText('Read more') + expect(readMore).toBeInTheDocument() + const button = screen.getByRole('button') + expect(button).toBeInTheDocument() + fireEvent.click(button) + const percent = screen.getByText(/65%/) + expect(percent).toBeInTheDocument() + }) }) diff --git a/adhocracy4/comments_async/static/comments_async/ai_report.jsx b/adhocracy4/comments_async/static/comments_async/ai_report.jsx index c57556c99..b80f9b226 100644 --- a/adhocracy4/comments_async/static/comments_async/ai_report.jsx +++ b/adhocracy4/comments_async/static/comments_async/ai_report.jsx @@ -3,9 +3,10 @@ import django from 'django' import { SwitchButton } from '../../../static/SwitchButton' const translated = { - intro: django.pgettext('defakts', 'This comment contains disinformation. Defakts uses an Artificial ' + - 'Intelligence to scan content for disinformation. Disinformation often shows ' + - 'certain characteristics that allow for a reliable identification.'), + expandableBar: django.pgettext('defakts', 'The defakt AI has found evidence of disinformation.'), + intro: django.pgettext('defakts', 'Defakts uses artificial intelligence to check content for disinformation based on certain linguistic characteristics.'), + 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.'), + 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.'), ariaReadMore: django.pgettext('defakts', 'Click to view the AI explanation for reporting this comment.'), ariaReadLess: django.pgettext('defakts', 'Click to hide the AI explanation for reporting this comment.'), readMore: django.pgettext('defakts', 'Read more'), @@ -14,7 +15,7 @@ const translated = { hideInfoSwitch: django.pgettext('defakts', 'Hide AI info from users') } -export const AiReport = ({ Report, notificationPk, toggleShowAiReport }) => { +export const AiReport = ({ report, notificationPk, toggleShowAiReport }) => { const [isExpanded, setIsExpanded] = useState() const toggleExpand = () => { @@ -32,9 +33,47 @@ export const AiReport = ({ Report, notificationPk, toggleShowAiReport }) => { ) + const confidenceToPercent = (confidence) => { + const percentFormat = new Intl.NumberFormat('default', { + style: 'percent', + minimumFractionDigits: 0, + maximumFractionDigits: 0 + }) + return percentFormat.format(confidence) + } + + const extractLabelWords = (label) => { + const words = report.explanation[label] + return words.slice(0, 3).map(word => word[0]).join(', ') + } + + const renderExplanation = ( +
    + {report.label.map(([key, description], index) => ( +
  • + {description.charAt(0).toUpperCase() + description.slice(1)} + ({confidenceToPercent(report.confidence[index])}): + {extractLabelWords(key)} +
  • + ))} +
+ ) + + const renderCTA = () => { + // replace "[FAQs]" with anchor + const [preText, placeholderText, postText] = translated.cta.split(/(\[.*?\])/) + // remove square brackets + const anchorText = placeholderText.replace(/\[|\]/g, '') + return ( + <> + {preText} {anchorText} {postText} + + ) + } + return ( -
-
+
+
{ {!isExpanded ? ( -

{translated.intro} {toggleReadMore}

+

{translated.expandableBar} {toggleReadMore}

) : (
-

{translated.intro}

-

{Report.explanation} {toggleReadMore}

+

{translated.expandableBar}

+

+ {translated.intro} + {translated.confidenceScore} +

+ {renderExplanation} +

+ {renderCTA()} {toggleReadMore} +

)}
{toggleShowAiReport && -
+
diff --git a/adhocracy4/comments_async/static/comments_async/comment.jsx b/adhocracy4/comments_async/static/comments_async/comment.jsx index 7407fdec9..55c800828 100644 --- a/adhocracy4/comments_async/static/comments_async/comment.jsx +++ b/adhocracy4/comments_async/static/comments_async/comment.jsx @@ -370,7 +370,7 @@ export default class Comment extends React.Component { {this.props.aiReport && this.props.aiReport.show_in_discussion && } {this.state.showModStatement && this.state.moderatorFeedback && diff --git a/adhocracy4/comments_async/static/comments_async/comment_list.jsx b/adhocracy4/comments_async/static/comments_async/comment_list.jsx index 3c3caee4e..a0c5799b0 100644 --- a/adhocracy4/comments_async/static/comments_async/comment_list.jsx +++ b/adhocracy4/comments_async/static/comments_async/comment_list.jsx @@ -7,6 +7,11 @@ const CommentList = (props) => {
    { props.comments.map((comment, index) => { + // don't show ai report if there are no labels + let aiReport = null + if (comment.ai_report && comment.ai_report.label.length > 0) { + aiReport = comment.ai_report + } return ( { wouldHaveCommentingPermission={props.wouldHaveCommentingPermission} projectIsPublic={props.projectIsPublic} moderatorFeedback={comment.moderator_feedback ? comment.moderator_feedback : null} - aiReport={comment.ai_report ? comment.ai_report : null} + aiReport={aiReport} useTermsOfUse={props.useTermsOfUse} agreedTermsOfUse={props.agreedTermsOfUse} orgTermsUrl={props.orgTermsUrl} diff --git a/changelog/8372.md b/changelog/8372.md new file mode 100644 index 000000000..572f8aba5 --- /dev/null +++ b/changelog/8372.md @@ -0,0 +1,5 @@ +### Changed + +- redesign AI report to be readable +- only show AI report when there's a useful label (catnodecis and catneutral don't +count as labels)