Skip to content

Commit 53399ab

Browse files
authored
Merge pull request #95 from LuCEresearchlab/tagging-cluster-data-rework
Tagging cluster data rework
2 parents 7e86cb9 + 49da111 commit 53399ab

File tree

13 files changed

+390
-218
lines changed

13 files changed

+390
-218
lines changed

frontend/src/components/v2/cluster_handler_component/ClusterHandler.tsx

Lines changed: 58 additions & 179 deletions
Large diffs are not rendered by default.
Lines changed: 141 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,141 @@
1+
import {Draggable, DraggableProvided, DraggableStateSnapshot} from "react-beautiful-dnd";
2+
import {Button, Paper} from "@material-ui/core";
3+
import {Eject} from "@material-ui/icons";
4+
import React from "react";
5+
import {Result, ResultCluster} from "./ClusterHandler";
6+
import {Answer, Cluster} from "../../../interfaces/Dataset";
7+
import {getDatasetId, getQuestion, TaggingSession} from "../../../model/TaggingSession";
8+
import {TaggingClusterSessionDispatch} from "../../../model/TaggingClusterSession";
9+
import stringEquals from "../../../util/StringEquals";
10+
import {setClusters} from "../../../model/TaggingClusterSessionDispatch";
11+
import {postClusters} from "../../../helpers/PostHelper";
12+
import {LIGHT_GREY} from "../../../util/Colors";
13+
14+
15+
// @ts-ignore
16+
import Highlightable from "highlightable";
17+
18+
interface Input {
19+
resultCluster: ResultCluster,
20+
idx: number,
21+
taggingSession: TaggingSession,
22+
clusters: Cluster[],
23+
result: Result,
24+
query: string,
25+
26+
setExtendedClusters(value: Result[]): void,
27+
28+
getSortedClusters(clusters: Cluster[], query: string): Result[],
29+
30+
dispatchTaggingClusterSession: React.Dispatch<TaggingClusterSessionDispatch>
31+
32+
33+
}
34+
35+
36+
function getItemStyle(isDragging: boolean, draggableStyle: any) {
37+
return {
38+
// some basic styles to make the items look a bit nicer
39+
userSelect: 'none',
40+
padding: '1em',
41+
margin: '1.5em',
42+
marginTop: 0,
43+
display: 'inline-flex',
44+
45+
// change background colour if dragging
46+
background: isDragging ? 'lightgreen' : LIGHT_GREY,
47+
48+
// styles we need to apply on draggable
49+
...draggableStyle
50+
}
51+
}
52+
53+
function popAnswer(clusters: Cluster[], result: Result, resultCluster: ResultCluster,
54+
taggingSession: TaggingSession,
55+
dispatchTaggingClusterSession: React.Dispatch<TaggingClusterSessionDispatch>): Cluster[] {
56+
const cluster: Cluster = clusters[result.cluster_idx]
57+
const answer_idx = cluster.cluster.findIndex(
58+
answer => stringEquals(answer.answer_id,
59+
resultCluster.item.answer.answer_id)
60+
)
61+
const popped: Answer[] = cluster.cluster.slice(answer_idx, answer_idx + 1)
62+
63+
const reduced_cluster: Answer[] = cluster.cluster.slice(0, answer_idx).concat(cluster.cluster.slice(answer_idx + 1))
64+
let new_clusters = [...clusters]
65+
new_clusters[result.cluster_idx] = {name: cluster.name, cluster: reduced_cluster}
66+
new_clusters.push({name: 'Cluster ' + new_clusters.length, cluster: popped})
67+
new_clusters = new_clusters.filter(cluster => cluster.cluster.length > 0) // remove empty clusters
68+
69+
dispatchTaggingClusterSession(setClusters(new_clusters))
70+
postClusters(
71+
getDatasetId(taggingSession),
72+
getQuestion(taggingSession).question_id,
73+
taggingSession.user_id,
74+
new_clusters
75+
)
76+
return new_clusters
77+
}
78+
79+
function DraggableCluster({
80+
taggingSession, resultCluster, idx, clusters, result, query, getSortedClusters,
81+
setExtendedClusters, dispatchTaggingClusterSession
82+
}: Input) {
83+
return (
84+
<Draggable
85+
key={resultCluster.item.answer.answer_id}
86+
draggableId={'' + resultCluster.item.answer.answer_id}
87+
index={idx}
88+
>
89+
{(provided1: DraggableProvided, snapshot: DraggableStateSnapshot) => (
90+
<Paper
91+
ref={provided1.innerRef}
92+
{...provided1.draggableProps}
93+
{...provided1.dragHandleProps}
94+
style={getItemStyle(
95+
snapshot.isDragging,
96+
provided1.draggableProps.style
97+
)}
98+
>
99+
{
100+
resultCluster.matches.length != 0 ?
101+
<Highlightable
102+
ranges={resultCluster.matches[0].indices.map(
103+
interval => {
104+
return {
105+
start: interval[0],
106+
end: interval[1],
107+
}
108+
})}
109+
enabled={false}
110+
text={resultCluster.item.answer.data}
111+
highlightStyle={{backgroundColor: '#EE000044'}}
112+
style={{width: '95%'}}
113+
/> :
114+
<div style={{width: '95%'}}>
115+
{resultCluster.item.answer.data}
116+
</div>
117+
}
118+
<Button
119+
style={{padding: 0}}
120+
onClick={() => {
121+
const new_clusters = popAnswer(
122+
clusters,
123+
result,
124+
resultCluster,
125+
taggingSession,
126+
dispatchTaggingClusterSession
127+
)
128+
setExtendedClusters(getSortedClusters(
129+
new_clusters,
130+
query
131+
))
132+
}}>
133+
<Eject/>
134+
</Button>
135+
</Paper>
136+
)}
137+
</Draggable>
138+
)
139+
}
140+
141+
export default DraggableCluster
Lines changed: 131 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,131 @@
1+
import {GREY} from "../../../util/Colors";
2+
import {Button, Card, CardContent, Collapse, TextField} from "@material-ui/core";
3+
import {ExpandLess, ExpandMore, ZoomIn} from "@material-ui/icons";
4+
import {DroppableProvided} from "react-beautiful-dnd";
5+
import React, {useEffect, useState} from "react";
6+
import {Result, ResultCluster} from "./ClusterHandler";
7+
8+
import {Cluster} from "../../../interfaces/Dataset";
9+
import {getDatasetId, getQuestion, TaggingSession} from "../../../model/TaggingSession";
10+
import {TaggingClusterSessionDispatch} from "../../../model/TaggingClusterSession";
11+
import {setClusters} from "../../../model/TaggingClusterSessionDispatch";
12+
import {postClusters} from "../../../helpers/PostHelper";
13+
import DraggableCluster from "./DraggableCluster";
14+
15+
16+
interface Input {
17+
taggingSession: TaggingSession,
18+
provided: DroppableProvided,
19+
clusters: Cluster[],
20+
result: Result,
21+
query: string,
22+
extendedClusters: Result[],
23+
24+
setCluster(value: number): void,
25+
26+
setExtendedClusters(value: Result[]): void,
27+
28+
getSortedClusters(clusters: Cluster[], query: string): Result[],
29+
30+
dispatchTaggingClusterSession: React.Dispatch<TaggingClusterSessionDispatch>
31+
}
32+
33+
34+
function shouldCollapse(query: string, result: Result): boolean {
35+
console.log('shouldNotCollapse')
36+
console.log(query, query.length == 0)
37+
for (let c of result.clusters) {
38+
if (c.matches.length != 0) return false
39+
}
40+
return query.length != 0
41+
}
42+
43+
function DroppableCluster({
44+
taggingSession,
45+
dispatchTaggingClusterSession,
46+
clusters,
47+
query,
48+
provided,
49+
extendedClusters,
50+
result,
51+
setCluster,
52+
setExtendedClusters,
53+
getSortedClusters
54+
}: Input) {
55+
56+
const [collapse, setCollapse] = useState<boolean>(false)
57+
58+
useEffect(() => {
59+
setCollapse(shouldCollapse(query, result))
60+
}, [extendedClusters])
61+
62+
const flipCollapse = () => {
63+
setCollapse(!collapse)
64+
}
65+
66+
return (
67+
<Card
68+
style={{
69+
backgroundColor: GREY, marginTop: '2.5em', display: 'flex',
70+
flexDirection: 'column'
71+
}}
72+
{...provided.droppableProps}
73+
ref={provided.innerRef}
74+
>
75+
<CardContent style={{
76+
paddingLeft: '0.5em', paddingRight: '0.5em', paddingBottom: '1em', display: 'flex',
77+
flexDirection: 'row'
78+
}}>
79+
<TextField style={{margin: 'auto', marginLeft: '2em'}} value={result.name}
80+
onChange={(e) => {
81+
clusters[result.cluster_idx].name = e.target.value
82+
postClusters(
83+
getDatasetId(taggingSession),
84+
getQuestion(taggingSession).question_id,
85+
taggingSession.user_id,
86+
[...clusters]
87+
)
88+
dispatchTaggingClusterSession(setClusters([...clusters]))
89+
setExtendedClusters(getSortedClusters([...clusters], query))
90+
}}/>
91+
<Button
92+
variant={'outlined'}
93+
title={`Switch to cluster ${result.cluster_idx + 1}`}
94+
onClick={() => setCluster(result.cluster_idx + 1)}
95+
>
96+
<ZoomIn/>
97+
</Button>
98+
<Button>
99+
{
100+
collapse ?
101+
<ExpandMore onClick={() => flipCollapse()}/> :
102+
<ExpandLess onClick={() => flipCollapse()}/>
103+
}
104+
</Button>
105+
</CardContent>
106+
<Collapse in={!collapse} timeout={0}>
107+
<CardContent style={{display: 'flex', flexDirection: 'column'}}>
108+
{
109+
result.clusters.map((resultCluster: ResultCluster, idx: number) =>
110+
<DraggableCluster
111+
key={'ClusterHandler|DraggableCluster|' + idx}
112+
taggingSession={taggingSession}
113+
clusters={clusters}
114+
result={result}
115+
resultCluster={resultCluster}
116+
idx={idx}
117+
query={query}
118+
getSortedClusters={getSortedClusters}
119+
setExtendedClusters={setExtendedClusters}
120+
dispatchTaggingClusterSession={dispatchTaggingClusterSession}
121+
/>
122+
)
123+
}
124+
{provided.placeholder}
125+
</CardContent>
126+
</Collapse>
127+
</Card>
128+
)
129+
}
130+
131+
export default DroppableCluster

frontend/src/components/v2/tagger_component/ClusterView.tsx

Lines changed: 15 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,5 @@
11
import React, {useEffect} from "react"
2-
import {Answer} from "../../../interfaces/Dataset";
2+
import {Answer, Cluster} from "../../../interfaces/Dataset";
33
import {rangesCompressor} from "../../../util/RangeCompressor";
44
import {HighlightRange, HighlightRangeColor} from "../../../interfaces/HighlightRange";
55
import {Button, Paper} from "@material-ui/core";
@@ -31,18 +31,23 @@ interface Input {
3131

3232
function ClusterView({taggingClusterSession, dispatchTaggingClusterSession}: Input) {
3333

34+
const currentCluster: Cluster = getCurrentCluster(taggingClusterSession)
35+
// TODO: use name field
36+
3437
return (
3538
<div>
3639
{
37-
getCurrentCluster(taggingClusterSession).map((answer: Answer, index: number) =>
38-
<ClusterItem
39-
key={"ClusterItem|Answer|" + answer.answer_id}
40-
answer={answer}
41-
taggingClusterSession={taggingClusterSession}
42-
dispatchTaggingClusterSession={dispatchTaggingClusterSession}
43-
displayKey={index + 1}
44-
/>
45-
)}
40+
currentCluster
41+
.cluster
42+
.map((answer: Answer, index: number) =>
43+
<ClusterItem
44+
key={"ClusterItem|Answer|" + answer.answer_id}
45+
answer={answer}
46+
taggingClusterSession={taggingClusterSession}
47+
dispatchTaggingClusterSession={dispatchTaggingClusterSession}
48+
displayKey={index + 1}
49+
/>
50+
)}
4651
</div>
4752
)
4853
}

frontend/src/components/v2/tagger_component/MisconceptionView.tsx

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -80,7 +80,7 @@ function MisconceptionView(
8080

8181
const getNewRangesList = (element: (string | null), index: number) => {
8282
if (taggingClusterSession.tags[0] != null) // delete highlighting if selecting with NoMisconception
83-
return [...Array(getCurrentCluster(taggingClusterSession).length)].map(() => [])
83+
return [...Array(getCurrentCluster(taggingClusterSession).cluster.length)].map(() => [])
8484
let new_ranges_list = []
8585
for (let ranges of taggingClusterSession.rangesList)
8686
new_ranges_list.push(highlightRangesColorUpdating(

frontend/src/components/v2/tagger_component/StaticSelectorView.tsx

Lines changed: 5 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -39,9 +39,12 @@ function StaticSelectorView({
3939

4040
const getNewRangesList = (element: (string | null), index: number) => {
4141

42-
if (isNoMisconception(element)) return [...Array(getCurrentCluster(taggingClusterSession).length)].map(() => [])
42+
if (isNoMisconception(element))
43+
return [...Array(getCurrentCluster(taggingClusterSession).cluster.length)].map(() => [])
44+
4345
if (handledIndex != 0 && taggingClusterSession.tags[0] != null)
44-
return [...Array(getCurrentCluster(taggingClusterSession).length)].map(() => [])
46+
return [...Array(getCurrentCluster(taggingClusterSession).cluster.length)].map(() => [])
47+
4548
let new_ranges_list = []
4649
for (let ranges of taggingClusterSession.rangesList)
4750
new_ranges_list.push(highlightRangesColorUpdating(

frontend/src/helpers/PostHelper.ts

Lines changed: 5 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,5 @@
11
import {HighlightRange} from "../interfaces/HighlightRange";
2-
import {Answer} from "../interfaces/Dataset";
2+
import {Cluster} from "../interfaces/Dataset";
33

44

55
const {TAGGING_SERVICE_URL} = require('../../config.json')
@@ -44,7 +44,10 @@ export function postHelper(dataset_id: string,
4444
export function postClusters(dataset_id: string,
4545
question_id: string,
4646
user_id: string,
47-
clusters: Answer[][]) {
47+
clusters: Cluster[]) {
48+
console.log(`posting to ${TAGGING_SERVICE_URL}/clusters/dataset/${dataset_id}/question/${question_id}/user/${
49+
user_id}`)
50+
console.log(clusters)
4851
post(`${TAGGING_SERVICE_URL}/clusters/dataset/${dataset_id}/question/${question_id}/user/${user_id}`,
4952
clusters)
5053
}

frontend/src/interfaces/Dataset.ts

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -25,3 +25,8 @@ export interface Answer {
2525
"picked": boolean,
2626
"matches_expected": boolean
2727
}
28+
29+
export interface Cluster {
30+
"name": string,
31+
"cluster": Answer[]
32+
}

0 commit comments

Comments
 (0)