Skip to content

Commit ae5b967

Browse files
authored
feat: plot of idea ratings (#25)
1 parent c74f598 commit ae5b967

File tree

8 files changed

+2212
-31
lines changed

8 files changed

+2212
-31
lines changed

package.json

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -37,12 +37,15 @@
3737
"@types/node": "17.0.45",
3838
"@types/react": "18.2.21",
3939
"@types/react-dom": "18.2.7",
40+
"@types/react-plotly.js": "^2.6.0",
4041
"i18next": "22.5.1",
4142
"immutable": "4.3.4",
43+
"plotly.js": "^2.26.0",
4244
"qs": "6.11.2",
4345
"react": "18.2.0",
4446
"react-dom": "18.2.0",
4547
"react-i18next": "12.3.1",
48+
"react-plotly.js": "^2.6.0",
4649
"react-router-dom": "^6.14.2",
4750
"typescript": "4.9.5"
4851
},

src/config/appDataTypes.ts

Lines changed: 8 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -4,6 +4,7 @@ import { AppDataRecord } from '@graasp/sdk/frontend';
44
import { List } from 'immutable';
55

66
import { IdeationState } from '@/interfaces/ideation';
7+
import { NoveltyRelevanceRatings } from '@/interfaces/ratings';
78

89
export enum AppDataTypes {
910
Idea = 'idea',
@@ -12,17 +13,21 @@ export enum AppDataTypes {
1213
Ratings = 'ratings',
1314
}
1415

15-
export type IdeaData = {
16+
export type IdeaData<T = NoveltyRelevanceRatings> = {
1617
idea: string;
1718
round?: number;
1819
bot?: boolean;
1920
parentId?: string;
2021
encoding?: 'text' | 'markdown';
22+
ratings?: T;
2123
};
2224

23-
export type AnonymousIdeaData = IdeaData & { id: string };
25+
export type AnonymousIdeaData<RatingsT = NoveltyRelevanceRatings> =
26+
IdeaData<RatingsT> & { id: string };
2427

25-
export type IdeasData = List<AnonymousIdeaData>;
28+
export type IdeasData<RatingsT = NoveltyRelevanceRatings> = List<
29+
AnonymousIdeaData<RatingsT>
30+
>;
2631

2732
export type IdeaAppData = AppDataRecord & {
2833
type: AppDataTypes.Idea;

src/modules/common/Synchronizer.tsx

Lines changed: 20 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -8,8 +8,13 @@ import { AppDataVisibility } from '@graasp/sdk';
88

99
import { List } from 'immutable';
1010

11-
import { IdeaAppData } from '@/config/appDataTypes';
11+
import {
12+
AppDataTypes,
13+
IdeaAppData,
14+
RatingsAppData,
15+
} from '@/config/appDataTypes';
1216
import { REFRESH_INTERVAL_MS } from '@/config/constants';
17+
import { NoveltyRelevanceRatings } from '@/interfaces/ratings';
1318
import { anonymizeIdeas } from '@/utils/ideas';
1419

1520
import { useAppDataContext } from '../context/AppDataContext';
@@ -43,7 +48,18 @@ const Synchronizer: FC<SynchronizerProps> = ({ sync }) => {
4348
);
4449

4550
const ideas = useMemo(
46-
() => appData.filter(({ type }) => type === 'idea') as List<IdeaAppData>,
51+
() =>
52+
appData.filter(
53+
({ type }) => type === AppDataTypes.Idea,
54+
) as List<IdeaAppData>,
55+
[appData],
56+
);
57+
58+
const ratings = useMemo(
59+
() =>
60+
appData.filter(({ type }) => type === AppDataTypes.Ratings) as List<
61+
RatingsAppData<NoveltyRelevanceRatings>
62+
>,
4763
[appData],
4864
);
4965

@@ -53,7 +69,7 @@ const Synchronizer: FC<SynchronizerProps> = ({ sync }) => {
5369
const newIdeasIds = ideas.map(({ id }) => id).sort();
5470
if (ideas && !ideasIds.equals(newIdeasIds)) {
5571
setIdeasIds(newIdeasIds);
56-
const anonymousIdeas = anonymizeIdeas(ideas);
72+
const anonymousIdeas = anonymizeIdeas(ideas, ratings);
5773
if (setId) {
5874
patchAppData({
5975
id: setId,
@@ -72,7 +88,7 @@ const Synchronizer: FC<SynchronizerProps> = ({ sync }) => {
7288
}
7389
}
7490
}
75-
}, [ideas, ideasIds, patchAppData, postAppData, setId, sync]);
91+
}, [ideas, ideasIds, patchAppData, postAppData, ratings, setId, sync]);
7692

7793
return (
7894
<Stack width="100%" direction="row" spacing={1}>

src/modules/main/ideasView/AnonymousIdeasView.tsx

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -7,6 +7,8 @@ import { AppDataTypes, IdeaSetAppData } from '@/config/appDataTypes';
77
import { useAppDataContext } from '@/modules/context/AppDataContext';
88
import { useSettings } from '@/modules/context/SettingsContext';
99

10+
import RatingsPlot from './RatingsPlot';
11+
1012
const AnonymousIdeasView = (): JSX.Element => {
1113
// const { t } = useTranslation();
1214
const { appData } = useAppDataContext();
@@ -47,6 +49,7 @@ const AnonymousIdeasView = (): JSX.Element => {
4749
return (
4850
<Container>
4951
<DataGrid columns={columns} rows={ideasTable.toArray()} />
52+
<RatingsPlot />
5053
</Container>
5154
);
5255
};

src/modules/main/ideasView/IdeasView.tsx

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -9,6 +9,8 @@ import { List, RecordOf } from 'immutable';
99
import { IdeaAppData } from '@/config/appDataTypes';
1010
import { useAppDataContext } from '@/modules/context/AppDataContext';
1111

12+
import RatingsPlot from './RatingsPlot';
13+
1214
const IdeasView = (): JSX.Element => {
1315
const { t } = useTranslation();
1416
const { appData } = useAppDataContext();
@@ -45,6 +47,7 @@ const IdeasView = (): JSX.Element => {
4547
return (
4648
<Container>
4749
<DataGrid columns={columns} rows={ideasTable.toArray()} />
50+
<RatingsPlot />
4851
</Container>
4952
);
5053
};
Lines changed: 55 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,55 @@
1+
import { FC, useMemo } from 'react';
2+
import Plot from 'react-plotly.js';
3+
4+
import Container from '@mui/material/Container';
5+
import { useTheme } from '@mui/material/styles';
6+
7+
import { AppDataTypes, IdeaSetAppData } from '@/config/appDataTypes';
8+
import { useAppDataContext } from '@/modules/context/AppDataContext';
9+
import { useSettings } from '@/modules/context/SettingsContext';
10+
11+
const RatingsPlot: FC = () => {
12+
const { appData } = useAppDataContext();
13+
const { orchestrator } = useSettings();
14+
const theme = useTheme();
15+
const ideas = useMemo(
16+
() =>
17+
(
18+
appData.find(
19+
({ creator, type }) =>
20+
creator?.id === orchestrator.id && type === AppDataTypes.IdeaSet,
21+
) as IdeaSetAppData
22+
)?.data.ideas,
23+
[appData, orchestrator.id],
24+
);
25+
26+
const x = ideas.map(({ ratings }) => ratings?.novelty || 0).toArray();
27+
const y = ideas.map(({ ratings }) => ratings?.usefulness || 0).toArray();
28+
const labels = ideas.map(({ idea }) => idea).toArray();
29+
30+
return (
31+
<Container>
32+
<Plot
33+
data={[
34+
{
35+
x,
36+
y,
37+
text: labels,
38+
type: 'scatter',
39+
mode: 'markers',
40+
marker: { color: theme.palette.primary.main },
41+
},
42+
]}
43+
layout={{
44+
// width: 600,
45+
height: 800,
46+
title: 'Idea ratings',
47+
xaxis: { title: 'Novelty' },
48+
yaxis: { title: 'Usefulness' },
49+
}}
50+
/>
51+
</Container>
52+
);
53+
};
54+
55+
export default RatingsPlot;

src/utils/ideas.ts

Lines changed: 29 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -7,18 +7,46 @@ import {
77
CurrentStateAppData,
88
IdeaAppData,
99
IdeasData,
10+
RatingsAppData,
1011
} from '@/config/appDataTypes';
12+
import { NoveltyRelevanceRatings } from '@/interfaces/ratings';
1113

12-
export const anonymizeIdeas = (ideas: List<IdeaAppData>): IdeasData =>
14+
const average = (array: number[]): number => {
15+
if (array.length > 0) {
16+
return array.reduce((a, b) => a + b) / array.length;
17+
}
18+
return 0;
19+
};
20+
21+
export const anonymizeIdeas = (
22+
ideas: List<IdeaAppData>,
23+
ratings?: List<RatingsAppData<NoveltyRelevanceRatings>>,
24+
): IdeasData =>
1325
List(
1426
ideas.map((ideaData) => {
1527
const { idea, round, parentId, encoding } = ideaData.data;
28+
let r: NoveltyRelevanceRatings | undefined;
29+
if (ratings) {
30+
const listOfRatings = ratings
31+
.filter(({ data }) => data.ideaRef === ideaData.id)
32+
.map(({ data }) => data.ratings);
33+
const usefulnessList: number[] = [];
34+
const noveltyList: number[] = [];
35+
listOfRatings.forEach(({ usefulness, novelty }) => {
36+
if (usefulness) usefulnessList.push(usefulness);
37+
if (novelty) noveltyList.push(novelty);
38+
});
39+
const meanUsefulness = average(usefulnessList);
40+
const meanNovelty = average(noveltyList);
41+
r = { novelty: meanNovelty, usefulness: meanUsefulness };
42+
}
1643
return {
1744
id: ideaData.id,
1845
idea,
1946
round,
2047
parentId,
2148
encoding,
49+
ratings: r,
2250
};
2351
}),
2452
);

0 commit comments

Comments
 (0)