Projekt

Obecné

Profil

Stáhnout (13.7 KB) Statistiky
| Větev: | Tag: | Revize:
1 acb8a961 Dominik Poch
import React, { createContext, useEffect, useState } from 'react';
2 bcbaa7d3 Dominik Poch
import {
3
    AnnotationInfo,
4
    ETagSentiment,
5
    ETagType,
6 b51488cd Jaroslav Hrubý
    MarkAnnotationDoneRequest,
7 bcbaa7d3 Dominik Poch
    TagInstanceInfo,
8
} from '../api';
9 9a41c02f Dominik Poch
import { Tag } from '../components/types/tag';
10 b51488cd Jaroslav Hrubý
import { annotationController } from '../controllers';
11 4bc99591 Lukáš Vlček
import { GetSelectionInfo } from '../utils/selectionUtils';
12 b51488cd Jaroslav Hrubý
import { ShowConfirm, ShowConfirmDelete, ShowToast } from '../utils/alerts';
13
import { useRouter } from 'next/router';
14 c057279b Lukáš Vlček
15 acb8a961 Dominik Poch
/**
16
 * Interface of an annotation context provider.
17
 */
18 c057279b Lukáš Vlček
interface IAnnotationContextProvider {
19 acb8a961 Dominik Poch
    /**
20
     * Tags managed by the context.
21
     */
22 c057279b Lukáš Vlček
    tags: TagInstanceInfo[] | null;
23 acb8a961 Dominik Poch
24 dee53692 Jaroslav Hrubý
    /**
25
     * Submitting boolean
26
     */
27
    submitting: boolean;
28
29 acb8a961 Dominik Poch
    /**
30
     * Sets new tags.
31
     * @param newTags An array of new tags.
32
     */
33 c057279b Lukáš Vlček
    setTags: (newTags: TagInstanceInfo[] | null) => void;
34 acb8a961 Dominik Poch
35
    /**
36
     * Adds occurrence to an annotation.
37
     * @param tag Tag whose occurrence should be added.
38
     */
39 c73aecde Dominik Poch
    addOccurrence: (tag: Tag) => void;
40 acb8a961 Dominik Poch
41
    /**
42
     * Deletes an occurrence of an annotation.
43
     * @param occurrence Occurrence that should be deleted.
44
     */
45 9a41c02f Dominik Poch
    deleteOccurrence: (occurrence: TagInstanceInfo) => void;
46 acb8a961 Dominik Poch
47 b51488cd Jaroslav Hrubý
    /**
48
     * Finishes annotation of document (marked as DONE).
49
     */
50
    finishAnnotation: () => void;
51
52 acb8a961 Dominik Poch
    /**
53
     * Changes a position of an occurrence of an annotation.
54
     * @param occurrence Occurrence whose position should be changed.
55
     * @param newValue New value of the position.
56
     */
57 9a41c02f Dominik Poch
    changePosition: (occurrence: TagInstanceInfo, newValue: number) => void;
58 acb8a961 Dominik Poch
59
    /**
60
     * Changes a length of an occurrence of an annotation.
61
     * @param occurrence Occurrence whose length should be changed.
62
     * @param newValue New value of the length.
63
     */
64 9a41c02f Dominik Poch
    changeLength: (occurrence: TagInstanceInfo, newValue: number) => void;
65 7652cf88 Lukáš Vlček
66 812ee966 Dominik Poch
    /**
67
     * Changes a note of an occurrence of an annotation.
68
     * @param occurrence Occurrence whose note should be changed.
69
     * @param newValue New value of the note.
70
     */
71
    changeNote: (occurrence: TagInstanceInfo, newValue: string) => void;
72
73 bcbaa7d3 Dominik Poch
    /**
74
     * Changes sentiment of an annotation.
75
     * @param occurrence Occurrence whose sentiment should be changed.
76
     * @param newValue New value of the sentiment.
77
     */
78
    changeSentiment: (occurrence: TagInstanceInfo, newValue: ETagSentiment) => void;
79
80 b80a6919 Lukáš Vlček
    makeOccurrenceFinal: (occurrence: TagInstanceInfo) => void;
81
82 7652cf88 Lukáš Vlček
    annotation: AnnotationInfo | null;
83 9a41c02f Dominik Poch
    mappedTags: Tag[] | null;
84 7652cf88 Lukáš Vlček
    refreshAnnotation: () => void;
85 4bc99591 Lukáš Vlček
86
    markSelectedText: (
87
        tagId: string,
88
        subtagId: string | null,
89
        instanceID: string | null
90
    ) => void;
91 6973d036 Lukáš Vlček
92
    selectedInstanceId: string | null;
93
    setSelectedInstanceId: (newId: string | null) => void;
94
95
    selectedOccurrenceId: string | null;
96
    setSelectedOccurrenceId: (newId: string | null) => void;
97 b80a6919 Lukáš Vlček
98
    isFinal: boolean;
99 c057279b Lukáš Vlček
}
100
101 acb8a961 Dominik Poch
/**
102
 * The annotation context that manages active annotations.
103
 */
104 c057279b Lukáš Vlček
export const AnnotationContext = createContext<IAnnotationContextProvider>({
105 acb8a961 Dominik Poch
    /**
106
     * Default tags.
107
     */
108 c057279b Lukáš Vlček
    tags: null,
109 acb8a961 Dominik Poch
110 dee53692 Jaroslav Hrubý
    /**
111
     * Submitting boolean
112
     */
113
    submitting: false,
114
115 acb8a961 Dominik Poch
    /**
116
     * Default implementation of setTags method.
117
     * @param v Array of new tags.
118
     */
119 c057279b Lukáš Vlček
    setTags: (v) => {
120
        return;
121
    },
122 acb8a961 Dominik Poch
123
    /**
124
     * Default implementation of addOccurrence method.
125
     * @param tag The tag with new occurrence.
126
     */
127 c73aecde Dominik Poch
    addOccurrence: (tag: Tag) => {
128 c057279b Lukáš Vlček
        return;
129
    },
130 acb8a961 Dominik Poch
131
    /**
132
     * Default implementation of deleteOccurrence method.
133
     * @param occurrence Occurrence that should be deleted.
134
     */
135 9a41c02f Dominik Poch
    deleteOccurrence: (occurrence: TagInstanceInfo) => {
136 c057279b Lukáš Vlček
        return;
137
    },
138 acb8a961 Dominik Poch
139 b51488cd Jaroslav Hrubý
    /**
140
     * Finishes annotation of document (marked as DONE).
141
     */
142
    finishAnnotation: () => {
143
        return;
144
    },
145
146 acb8a961 Dominik Poch
    /**
147
     * Default implementation of changePosition method.
148
     * @param occurrence Occurrence whose position should be changed.
149
     * @param newValue A new position.
150
     */
151 9a41c02f Dominik Poch
    changePosition: (occurrence: TagInstanceInfo, newValue: number) => {
152 c057279b Lukáš Vlček
        return;
153
    },
154 acb8a961 Dominik Poch
155
    /**
156
     * Default implementation of changeLength method.
157
     * @param occurrence Occurrence whose length should be changed.
158
     * @param newValue A new length.
159
     */
160 9a41c02f Dominik Poch
    changeLength: (occurrence: TagInstanceInfo, newValue: number) => {
161 c057279b Lukáš Vlček
        return;
162
    },
163 7652cf88 Lukáš Vlček
164 812ee966 Dominik Poch
    /**
165
     * Changes a note of an occurrence of an annotation.
166
     * @param occurrence Occurrence whose note should be changed.
167
     * @param newValue New value of the note.
168
     */
169
    changeNote: (occurrence: TagInstanceInfo, newValue: string) => {
170
        return;
171
    },
172
173 bcbaa7d3 Dominik Poch
    /**
174
     * Changes sentiment of an annotation.
175
     * @param occurrence Occurrence whose sentiment should be changed.
176
     * @param newValue New value of the sentiment.
177
     */
178
    changeSentiment: (occurrence: TagInstanceInfo, newValue: ETagSentiment) => {
179
        return;
180
    },
181
182 7652cf88 Lukáš Vlček
    annotation: null,
183 9a41c02f Dominik Poch
    mappedTags: null,
184 7652cf88 Lukáš Vlček
    refreshAnnotation: () => {
185
        return;
186
    },
187 4bc99591 Lukáš Vlček
188
    markSelectedText: () => {
189
        return;
190
    },
191 6973d036 Lukáš Vlček
192
    selectedInstanceId: null,
193
    setSelectedInstanceId: (newId: string | null) => {
194
        return;
195
    },
196
197
    selectedOccurrenceId: null,
198
    setSelectedOccurrenceId: (newId: string | null) => {
199
        return;
200
    },
201 b80a6919 Lukáš Vlček
202
    isFinal: false,
203
    makeOccurrenceFinal: (occurrence: TagInstanceInfo) => {
204
        return;
205
    },
206 c057279b Lukáš Vlček
});
207
208 acb8a961 Dominik Poch
/**
209
 * Provider of the annotation context.
210
 * @param props Children that should have access to the annotation context.
211
 * @returns Prepared html of the provider.
212
 */
213 7595035c Lukáš Vlček
const AnnotationProvider = (props: {
214
    children: React.ReactNode;
215
    annotationId: string;
216 048b4bc0 Lukáš Vlček
    isFinal: boolean;
217 7595035c Lukáš Vlček
}) => {
218 7652cf88 Lukáš Vlček
    const [annotation, setAnnotation] = useState<AnnotationInfo | null>(null);
219
220 acb8a961 Dominik Poch
    /**
221
     * Tags managed by the context.
222
     */
223 c057279b Lukáš Vlček
    const [tags, setTags] = useState<TagInstanceInfo[] | null>(null);
224
225 9a41c02f Dominik Poch
    const [mappedTags, setMappedTags] = useState<Tag[] | null>(null);
226 6973d036 Lukáš Vlček
    const [selectedInstanceId, setSelectedInstanceId] = useState<string | null>(null);
227
    const [selectedOccurrenceId, setSelectedOccurrenceId] = useState<string | null>(null);
228 9a41c02f Dominik Poch
229 dee53692 Jaroslav Hrubý
    const [submitting, setSubmitting] = useState(false);
230
231 b51488cd Jaroslav Hrubý
    const router = useRouter();
232
233 4bc99591 Lukáš Vlček
    async function markSelectedText(
234
        tagId: string,
235
        subtagId: string | null,
236
        instanceId: string | null
237
    ) {
238 dee53692 Jaroslav Hrubý
        setSubmitting(true);
239 4bc99591 Lukáš Vlček
        if (!annotation) {
240
            console.log('annotation not found');
241
            return;
242
        }
243
244
        const selectionInfo = GetSelectionInfo(annotation);
245
        if (!selectionInfo) {
246
            console.log(
247
                'not able to continue, selection processing not completed successfully'
248
            );
249 eb2b4573 Jaroslav Hrubý
            ShowToast('Není označen žádný text pro přidělení značky', 'warning');
250
            setSubmitting(false);
251 4bc99591 Lukáš Vlček
            return;
252
        }
253
254
        const id = subtagId ?? tagId;
255
        const type: ETagType = subtagId == null ? ETagType.Tag : ETagType.Subtag;
256 dee53692 Jaroslav Hrubý
        const res = await annotationController
257 048b4bc0 Lukáš Vlček
            .annotationAnnotationIdPost(props.annotationId, props.isFinal, {
258 4bc99591 Lukáš Vlček
                id: id,
259
                instanceId,
260
                type,
261
                position: selectionInfo.startPositionOriginalDocument,
262
                length: selectionInfo.selectionLengthOriginalDocument,
263 ba94b8c1 Lukáš Vlček
                selectedText: selectionInfo.selectedText,
264 dee53692 Jaroslav Hrubý
            })
265 5ef75299 Lukáš Vlček
            .catch((e) =>
266
                ShowToast('Tato část textu je již touto značkou anotována', 'warning')
267
            );
268 4bc99591 Lukáš Vlček
269
        await refreshAnnotation();
270
    }
271
272 acb8a961 Dominik Poch
    /**
273
     * Default implementation of addOccurrence method.
274
     * @param tag The tag with new occurrence.
275
     */
276 4bc99591 Lukáš Vlček
    const addOccurrence = async (tag: Tag) => {
277
        await markSelectedText(tag.tagId, tag.subtagId ?? null, tag.instanceId);
278 acb8a961 Dominik Poch
    };
279
280
    /**
281
     * Deletes an occurrence of an annotation.
282
     * @param occurrence Occurrence that should be deleted.
283
     */
284 9fdb7d55 Lukáš Vlček
    const deleteOccurrence = async (occurrence: TagInstanceInfo) => {
285
        if (!occurrence.occurenceId) {
286
            console.log('invalid occurrence');
287
            return;
288
        }
289
290 dee53692 Jaroslav Hrubý
        ShowConfirmDelete(() => {
291
            annotationController
292
                .annotationAnnotationIdOccurenceIdDelete(
293
                    props.annotationId,
294 b80a6919 Lukáš Vlček
                    occurrence.occurenceId ?? '',
295
                    props.isFinal
296 dee53692 Jaroslav Hrubý
                )
297
                .then(() => refreshAnnotation());
298
        }, 'značku');
299 acb8a961 Dominik Poch
    };
300
301 b51488cd Jaroslav Hrubý
    /**
302
     * Finishes annotation of document (marked as DONE).
303
     */
304
    const finishAnnotation = () => {
305
        const req: MarkAnnotationDoneRequest = { done: true };
306
        ShowConfirm(
307
            () =>
308
                annotationController
309 048b4bc0 Lukáš Vlček
                    .annotationAnnotationIdDonePut(props.annotationId, props.isFinal, req)
310 b51488cd Jaroslav Hrubý
                    .then(() => {
311 048b4bc0 Lukáš Vlček
                        if (props.isFinal) {
312
                            router.push('/documents/admin');
313
                        } else {
314
                            router.push('/documents/annotator');
315
                        }
316
317 b51488cd Jaroslav Hrubý
                        ShowToast('Anotování bylo úspěšně dokončeno');
318
                    }),
319
            'dokončit anotování'
320
        );
321
    };
322
323 acb8a961 Dominik Poch
    /**
324
     * Changes a position of an occurrence of an annotation.
325
     * @param occurrence Occurrence whose position should be changed.
326
     * @param newValue New value of the position.
327
     */
328 9a41c02f Dominik Poch
    const changePosition = (occurrence: TagInstanceInfo, newValue: number) => {
329 acb8a961 Dominik Poch
        //TODO: Implement method (should use objects from server API)
330
    };
331
332
    /**
333
     * Changes a length of an occurrence of an annotation.
334
     * @param occurrence Occurrence whose length should be changed.
335
     * @param newValue New value of the length.
336
     */
337 9a41c02f Dominik Poch
    const changeLength = (occurrence: TagInstanceInfo, newValue: number) => {
338 acb8a961 Dominik Poch
        //TODO: Implement method (should use objects from server API)
339
    };
340
341 812ee966 Dominik Poch
    /**
342
     * Changes a note of an occurrence of an annotation.
343
     * @param occurrence Occurrence whose note should be changed.
344
     * @param newValue New value of the note.
345
     */
346
    const changeNote = async (occurrence: TagInstanceInfo, newValue: string) => {
347
        if (!occurrence.occurenceId) {
348
            console.log('invalid occurrence');
349
            return;
350
        }
351
352
        const postRes =
353
            await annotationController.annotationAnnotationIdTagOccurenceIdNotePost(
354
                props.annotationId,
355
                occurrence.occurenceId,
356 048b4bc0 Lukáš Vlček
                props.isFinal,
357 812ee966 Dominik Poch
                { note: newValue }
358
            );
359
360 89154cb1 Dominik Poch
        occurrence.note = newValue;
361 812ee966 Dominik Poch
    };
362
363 bcbaa7d3 Dominik Poch
    /**
364
     * Changes sentiment of an annotation.
365
     * @param occurrence Occurrence whose sentiment should be changed.
366
     * @param newValue New value of the sentiment.
367
     */
368
    const changeSentiment = async (
369
        occurrence: TagInstanceInfo,
370
        newValue: ETagSentiment
371
    ) => {
372
        if (!occurrence.instance) {
373
            console.log('invalid instance');
374
            return;
375
        }
376
377
        const putRes =
378
            await annotationController.annotationAnnotationIdInstanceIdSentimentPut(
379
                props.annotationId,
380
                occurrence.instance,
381 048b4bc0 Lukáš Vlček
                props.isFinal,
382 bcbaa7d3 Dominik Poch
                { sentiment: newValue }
383
            );
384
385 89154cb1 Dominik Poch
        occurrence.sentiment = newValue;
386 bcbaa7d3 Dominik Poch
    };
387
388 9a41c02f Dominik Poch
    const remapAnnotations = (data: AnnotationInfo) => {
389 4bc99591 Lukáš Vlček
        let map = new Map<string, Tag>();
390 9a41c02f Dominik Poch
        data.tagInstances?.forEach((tagInstance) => {
391 4bc99591 Lukáš Vlček
            if (map.has(tagInstance.instance ?? '-')) {
392
                let tag = map.get(tagInstance.instance ?? '-');
393 9a41c02f Dominik Poch
                tag!.occurrences = [...tag!.occurrences, tagInstance];
394
            } else {
395 4bc99591 Lukáš Vlček
                map.set(tagInstance.instance ?? '-', {
396
                    tagName: tagInstance.tagName ?? '',
397
                    subtagName: tagInstance.subTagName ?? null,
398 9a41c02f Dominik Poch
                    category: tagInstance.tagCategoryName ?? '',
399
                    occurrences: [tagInstance],
400 4bc99591 Lukáš Vlček
                    tagId: tagInstance.tagId ?? '',
401
                    instanceId: tagInstance.instance ?? '',
402
                    subtagId: tagInstance.subTagId ?? null,
403 9a41c02f Dominik Poch
                });
404
            }
405
        });
406
407
        setMappedTags(Array.from(map.values()));
408
    };
409
410 b80a6919 Lukáš Vlček
    async function makeOccurrenceFinal(occurrence: TagInstanceInfo) {
411
        if (!occurrence?.occurenceId) {
412
            return;
413
        }
414
415
        await annotationController.annotationAnnotationIdOccurenceIdFinalPut(
416
            props.annotationId,
417
            occurrence.occurenceId,
418
            { isFinal: true }
419
        );
420
421
        await refreshAnnotation();
422
    }
423
424 7652cf88 Lukáš Vlček
    async function refreshAnnotation() {
425
        const data = await annotationController.annotationAnnotationIdGet(
426 048b4bc0 Lukáš Vlček
            props.annotationId,
427
            props.isFinal
428 7652cf88 Lukáš Vlček
        );
429
430 9a41c02f Dominik Poch
        remapAnnotations(data.data);
431 7652cf88 Lukáš Vlček
        setAnnotation(data.data ?? null);
432 dee53692 Jaroslav Hrubý
        setSubmitting(false);
433 7652cf88 Lukáš Vlček
    }
434
435 acb8a961 Dominik Poch
    /**
436
     * Initializes the context.
437
     */
438 c057279b Lukáš Vlček
    useEffect(() => {
439 7652cf88 Lukáš Vlček
        refreshAnnotation();
440 7595035c Lukáš Vlček
    }, [props.annotationId]);
441 c057279b Lukáš Vlček
442
    return (
443
        <AnnotationContext.Provider
444
            value={{
445
                tags,
446 dee53692 Jaroslav Hrubý
                submitting,
447 c057279b Lukáš Vlček
                setTags,
448
                addOccurrence,
449
                deleteOccurrence,
450 b51488cd Jaroslav Hrubý
                finishAnnotation,
451 c057279b Lukáš Vlček
                changeLength,
452
                changePosition,
453 812ee966 Dominik Poch
                changeNote,
454 bcbaa7d3 Dominik Poch
                changeSentiment,
455 7652cf88 Lukáš Vlček
                refreshAnnotation,
456
                annotation,
457 9a41c02f Dominik Poch
                mappedTags,
458 4bc99591 Lukáš Vlček
                markSelectedText,
459 6973d036 Lukáš Vlček
                selectedInstanceId,
460
                setSelectedInstanceId,
461
                selectedOccurrenceId,
462
                setSelectedOccurrenceId,
463 b80a6919 Lukáš Vlček
                isFinal: props.isFinal,
464
                makeOccurrenceFinal,
465 c057279b Lukáš Vlček
            }}
466
        >
467
            {props.children}
468
        </AnnotationContext.Provider>
469
    );
470
};
471
472
export default AnnotationProvider;