Projekt

Obecné

Profil

Stáhnout (13 KB) Statistiky
| Větev: | Tag: | Revize:
1
import React, { createContext, useEffect, useState } from 'react';
2
import {
3
    AnnotationInfo,
4
    ETagSentiment,
5
    ETagType,
6
    MarkAnnotationDoneRequest,
7
    TagInstanceInfo,
8
} from '../api';
9
import { Tag } from '../components/types/tag';
10
import { annotationController } from '../controllers';
11
import { GetSelectionInfo } from '../utils/selectionUtils';
12
import { ShowConfirm, ShowConfirmDelete, ShowToast } from '../utils/alerts';
13
import { useRouter } from 'next/router';
14

    
15
/**
16
 * Interface of an annotation context provider.
17
 */
18
interface IAnnotationContextProvider {
19
    /**
20
     * Tags managed by the context.
21
     */
22
    tags: TagInstanceInfo[] | null;
23

    
24
    /**
25
     * Submitting boolean
26
     */
27
    submitting: boolean;
28

    
29
    /**
30
     * Sets new tags.
31
     * @param newTags An array of new tags.
32
     */
33
    setTags: (newTags: TagInstanceInfo[] | null) => void;
34

    
35
    /**
36
     * Adds occurrence to an annotation.
37
     * @param tag Tag whose occurrence should be added.
38
     */
39
    addOccurrence: (tag: Tag) => void;
40

    
41
    /**
42
     * Deletes an occurrence of an annotation.
43
     * @param occurrence Occurrence that should be deleted.
44
     */
45
    deleteOccurrence: (occurrence: TagInstanceInfo) => void;
46

    
47
    /**
48
     * Finishes annotation of document (marked as DONE).
49
     */
50
    finishAnnotation: () => void;
51

    
52
    /**
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
    changePosition: (occurrence: TagInstanceInfo, newValue: number) => void;
58

    
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
    changeLength: (occurrence: TagInstanceInfo, newValue: number) => void;
65

    
66
    /**
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
    /**
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
    annotation: AnnotationInfo | null;
81
    mappedTags: Tag[] | null;
82
    refreshAnnotation: () => void;
83

    
84
    markSelectedText: (
85
        tagId: string,
86
        subtagId: string | null,
87
        instanceID: string | null
88
    ) => void;
89

    
90
    selectedInstanceId: string | null;
91
    setSelectedInstanceId: (newId: string | null) => void;
92

    
93
    selectedOccurrenceId: string | null;
94
    setSelectedOccurrenceId: (newId: string | null) => void;
95
}
96

    
97
/**
98
 * The annotation context that manages active annotations.
99
 */
100
export const AnnotationContext = createContext<IAnnotationContextProvider>({
101
    /**
102
     * Default tags.
103
     */
104
    tags: null,
105

    
106
    /**
107
     * Submitting boolean
108
     */
109
    submitting: false,
110

    
111
    /**
112
     * Default implementation of setTags method.
113
     * @param v Array of new tags.
114
     */
115
    setTags: (v) => {
116
        return;
117
    },
118

    
119
    /**
120
     * Default implementation of addOccurrence method.
121
     * @param tag The tag with new occurrence.
122
     */
123
    addOccurrence: (tag: Tag) => {
124
        return;
125
    },
126

    
127
    /**
128
     * Default implementation of deleteOccurrence method.
129
     * @param occurrence Occurrence that should be deleted.
130
     */
131
    deleteOccurrence: (occurrence: TagInstanceInfo) => {
132
        return;
133
    },
134

    
135
    /**
136
     * Finishes annotation of document (marked as DONE).
137
     */
138
    finishAnnotation: () => {
139
        return;
140
    },
141

    
142
    /**
143
     * Default implementation of changePosition method.
144
     * @param occurrence Occurrence whose position should be changed.
145
     * @param newValue A new position.
146
     */
147
    changePosition: (occurrence: TagInstanceInfo, newValue: number) => {
148
        return;
149
    },
150

    
151
    /**
152
     * Default implementation of changeLength method.
153
     * @param occurrence Occurrence whose length should be changed.
154
     * @param newValue A new length.
155
     */
156
    changeLength: (occurrence: TagInstanceInfo, newValue: number) => {
157
        return;
158
    },
159

    
160
    /**
161
     * Changes a note of an occurrence of an annotation.
162
     * @param occurrence Occurrence whose note should be changed.
163
     * @param newValue New value of the note.
164
     */
165
    changeNote: (occurrence: TagInstanceInfo, newValue: string) => {
166
        return;
167
    },
168

    
169
    /**
170
     * Changes sentiment of an annotation.
171
     * @param occurrence Occurrence whose sentiment should be changed.
172
     * @param newValue New value of the sentiment.
173
     */
174
    changeSentiment: (occurrence: TagInstanceInfo, newValue: ETagSentiment) => {
175
        return;
176
    },
177

    
178
    annotation: null,
179
    mappedTags: null,
180
    refreshAnnotation: () => {
181
        return;
182
    },
183

    
184
    markSelectedText: () => {
185
        return;
186
    },
187

    
188
    selectedInstanceId: null,
189
    setSelectedInstanceId: (newId: string | null) => {
190
        return;
191
    },
192

    
193
    selectedOccurrenceId: null,
194
    setSelectedOccurrenceId: (newId: string | null) => {
195
        return;
196
    },
197
});
198

    
199
/**
200
 * Provider of the annotation context.
201
 * @param props Children that should have access to the annotation context.
202
 * @returns Prepared html of the provider.
203
 */
204
const AnnotationProvider = (props: {
205
    children: React.ReactNode;
206
    annotationId: string;
207
    isFinal: boolean;
208
}) => {
209
    const [annotation, setAnnotation] = useState<AnnotationInfo | null>(null);
210

    
211
    /**
212
     * Tags managed by the context.
213
     */
214
    const [tags, setTags] = useState<TagInstanceInfo[] | null>(null);
215

    
216
    const [mappedTags, setMappedTags] = useState<Tag[] | null>(null);
217
    const [selectedInstanceId, setSelectedInstanceId] = useState<string | null>(null);
218
    const [selectedOccurrenceId, setSelectedOccurrenceId] = useState<string | null>(null);
219

    
220
    const [submitting, setSubmitting] = useState(false);
221

    
222
    const router = useRouter();
223

    
224
    async function markSelectedText(
225
        tagId: string,
226
        subtagId: string | null,
227
        instanceId: string | null
228
    ) {
229
        setSubmitting(true);
230
        if (!annotation) {
231
            console.log('annotation not found');
232
            return;
233
        }
234

    
235
        const selectionInfo = GetSelectionInfo(annotation);
236
        if (!selectionInfo) {
237
            console.log(
238
                'not able to continue, selection processing not completed successfully'
239
            );
240
            ShowToast('Není označen žádný text pro přidělení značky', 'warning');
241
            setSubmitting(false);
242
            return;
243
        }
244

    
245
        const id = subtagId ?? tagId;
246
        const type: ETagType = subtagId == null ? ETagType.Tag : ETagType.Subtag;
247
        const res = await annotationController
248
            .annotationAnnotationIdPost(props.annotationId, props.isFinal, {
249
                id: id,
250
                instanceId,
251
                type,
252
                position: selectionInfo.startPositionOriginalDocument,
253
                length: selectionInfo.selectionLengthOriginalDocument,
254
                selectedText: selectionInfo.selectedText,
255
            })
256
            .catch((e) =>
257
                ShowToast('Tato část textu je již touto značkou anotována', 'warning')
258
            );
259

    
260
        await refreshAnnotation();
261
    }
262

    
263
    /**
264
     * Default implementation of addOccurrence method.
265
     * @param tag The tag with new occurrence.
266
     */
267
    const addOccurrence = async (tag: Tag) => {
268
        await markSelectedText(tag.tagId, tag.subtagId ?? null, tag.instanceId);
269
    };
270

    
271
    /**
272
     * Deletes an occurrence of an annotation.
273
     * @param occurrence Occurrence that should be deleted.
274
     */
275
    const deleteOccurrence = async (occurrence: TagInstanceInfo) => {
276
        if (!occurrence.occurenceId) {
277
            console.log('invalid occurrence');
278
            return;
279
        }
280

    
281
        ShowConfirmDelete(() => {
282
            annotationController
283
                .annotationAnnotationIdOccurenceIdDelete(
284
                    props.annotationId,
285
                    occurrence.occurenceId ?? ''
286
                )
287
                .then(() => refreshAnnotation());
288
        }, 'značku');
289
    };
290

    
291
    /**
292
     * Finishes annotation of document (marked as DONE).
293
     */
294
    const finishAnnotation = () => {
295
        const req: MarkAnnotationDoneRequest = { done: true };
296
        ShowConfirm(
297
            () =>
298
                annotationController
299
                    .annotationAnnotationIdDonePut(props.annotationId, props.isFinal, req)
300
                    .then(() => {
301
                        if (props.isFinal) {
302
                            router.push('/documents/admin');
303
                        } else {
304
                            router.push('/documents/annotator');
305
                        }
306

    
307
                        ShowToast('Anotování bylo úspěšně dokončeno');
308
                    }),
309
            'dokončit anotování'
310
        );
311
    };
312

    
313
    /**
314
     * Changes a position of an occurrence of an annotation.
315
     * @param occurrence Occurrence whose position should be changed.
316
     * @param newValue New value of the position.
317
     */
318
    const changePosition = (occurrence: TagInstanceInfo, newValue: number) => {
319
        //TODO: Implement method (should use objects from server API)
320
    };
321

    
322
    /**
323
     * Changes a length of an occurrence of an annotation.
324
     * @param occurrence Occurrence whose length should be changed.
325
     * @param newValue New value of the length.
326
     */
327
    const changeLength = (occurrence: TagInstanceInfo, newValue: number) => {
328
        //TODO: Implement method (should use objects from server API)
329
    };
330

    
331
    /**
332
     * Changes a note of an occurrence of an annotation.
333
     * @param occurrence Occurrence whose note should be changed.
334
     * @param newValue New value of the note.
335
     */
336
    const changeNote = async (occurrence: TagInstanceInfo, newValue: string) => {
337
        if (!occurrence.occurenceId) {
338
            console.log('invalid occurrence');
339
            return;
340
        }
341

    
342
        const postRes =
343
            await annotationController.annotationAnnotationIdTagOccurenceIdNotePost(
344
                props.annotationId,
345
                occurrence.occurenceId,
346
                props.isFinal,
347
                { note: newValue }
348
            );
349

    
350
        occurrence.note = newValue;
351
    };
352

    
353
    /**
354
     * Changes sentiment of an annotation.
355
     * @param occurrence Occurrence whose sentiment should be changed.
356
     * @param newValue New value of the sentiment.
357
     */
358
    const changeSentiment = async (
359
        occurrence: TagInstanceInfo,
360
        newValue: ETagSentiment
361
    ) => {
362
        if (!occurrence.instance) {
363
            console.log('invalid instance');
364
            return;
365
        }
366

    
367
        const putRes =
368
            await annotationController.annotationAnnotationIdInstanceIdSentimentPut(
369
                props.annotationId,
370
                occurrence.instance,
371
                props.isFinal,
372
                { sentiment: newValue }
373
            );
374

    
375
        occurrence.sentiment = newValue;
376
    };
377

    
378
    const remapAnnotations = (data: AnnotationInfo) => {
379
        let map = new Map<string, Tag>();
380
        data.tagInstances?.forEach((tagInstance) => {
381
            if (map.has(tagInstance.instance ?? '-')) {
382
                let tag = map.get(tagInstance.instance ?? '-');
383
                tag!.occurrences = [...tag!.occurrences, tagInstance];
384
            } else {
385
                map.set(tagInstance.instance ?? '-', {
386
                    tagName: tagInstance.tagName ?? '',
387
                    subtagName: tagInstance.subTagName ?? null,
388
                    category: tagInstance.tagCategoryName ?? '',
389
                    occurrences: [tagInstance],
390
                    tagId: tagInstance.tagId ?? '',
391
                    instanceId: tagInstance.instance ?? '',
392
                    subtagId: tagInstance.subTagId ?? null,
393
                });
394
            }
395
        });
396

    
397
        setMappedTags(Array.from(map.values()));
398
    };
399

    
400
    async function refreshAnnotation() {
401
        const data = await annotationController.annotationAnnotationIdGet(
402
            props.annotationId,
403
            props.isFinal
404
        );
405

    
406
        remapAnnotations(data.data);
407
        setAnnotation(data.data ?? null);
408
        setSubmitting(false);
409
    }
410

    
411
    /**
412
     * Initializes the context.
413
     */
414
    useEffect(() => {
415
        refreshAnnotation();
416
    }, [props.annotationId]);
417

    
418
    return (
419
        <AnnotationContext.Provider
420
            value={{
421
                tags,
422
                submitting,
423
                setTags,
424
                addOccurrence,
425
                deleteOccurrence,
426
                finishAnnotation,
427
                changeLength,
428
                changePosition,
429
                changeNote,
430
                changeSentiment,
431
                refreshAnnotation,
432
                annotation,
433
                mappedTags,
434
                markSelectedText,
435
                selectedInstanceId,
436
                setSelectedInstanceId,
437
                selectedOccurrenceId,
438
                setSelectedOccurrenceId,
439
            }}
440
        >
441
            {props.children}
442
        </AnnotationContext.Provider>
443
    );
444
};
445

    
446
export default AnnotationProvider;
(1-1/3)