Projekt

Obecné

Profil

Stáhnout (12.7 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
}) => {
208
    const [annotation, setAnnotation] = useState<AnnotationInfo | null>(null);
209

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

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

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

    
221
    const router = useRouter();
222

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

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

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

    
259
        await refreshAnnotation();
260
    }
261

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

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

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

    
290
    /**
291
     * Finishes annotation of document (marked as DONE).
292
     */
293
    const finishAnnotation = () => {
294
        const req: MarkAnnotationDoneRequest = { done: true };
295
        ShowConfirm(
296
            () =>
297
                annotationController
298
                    .annotationAnnotationIdDonePut(props.annotationId, req)
299
                    .then(() => {
300
                        router.push('/documents/annotator');
301
                        ShowToast('Anotování bylo úspěšně dokončeno');
302
                    }),
303
            'dokončit anotování'
304
        );
305
    };
306

    
307
    /**
308
     * Changes a position of an occurrence of an annotation.
309
     * @param occurrence Occurrence whose position should be changed.
310
     * @param newValue New value of the position.
311
     */
312
    const changePosition = (occurrence: TagInstanceInfo, newValue: number) => {
313
        //TODO: Implement method (should use objects from server API)
314
    };
315

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

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

    
336
        const postRes =
337
            await annotationController.annotationAnnotationIdTagOccurenceIdNotePost(
338
                props.annotationId,
339
                occurrence.occurenceId,
340
                { note: newValue }
341
            );
342

    
343
        occurrence.note = newValue;
344
    };
345

    
346
    /**
347
     * Changes sentiment of an annotation.
348
     * @param occurrence Occurrence whose sentiment should be changed.
349
     * @param newValue New value of the sentiment.
350
     */
351
    const changeSentiment = async (
352
        occurrence: TagInstanceInfo,
353
        newValue: ETagSentiment
354
    ) => {
355
        if (!occurrence.instance) {
356
            console.log('invalid instance');
357
            return;
358
        }
359

    
360
        const putRes =
361
            await annotationController.annotationAnnotationIdInstanceIdSentimentPut(
362
                props.annotationId,
363
                occurrence.instance,
364
                { sentiment: newValue }
365
            );
366

    
367
        occurrence.sentiment = newValue;
368
    };
369

    
370
    const remapAnnotations = (data: AnnotationInfo) => {
371
        let map = new Map<string, Tag>();
372
        data.tagInstances?.forEach((tagInstance) => {
373
            if (map.has(tagInstance.instance ?? '-')) {
374
                let tag = map.get(tagInstance.instance ?? '-');
375
                tag!.occurrences = [...tag!.occurrences, tagInstance];
376
            } else {
377
                map.set(tagInstance.instance ?? '-', {
378
                    tagName: tagInstance.tagName ?? '',
379
                    subtagName: tagInstance.subTagName ?? null,
380
                    category: tagInstance.tagCategoryName ?? '',
381
                    occurrences: [tagInstance],
382
                    tagId: tagInstance.tagId ?? '',
383
                    instanceId: tagInstance.instance ?? '',
384
                    subtagId: tagInstance.subTagId ?? null,
385
                });
386
            }
387
        });
388

    
389
        setMappedTags(Array.from(map.values()));
390
    };
391

    
392
    async function refreshAnnotation() {
393
        const data = await annotationController.annotationAnnotationIdGet(
394
            props.annotationId
395
        );
396

    
397
        remapAnnotations(data.data);
398
        setAnnotation(data.data ?? null);
399
        setSubmitting(false);
400
    }
401

    
402
    /**
403
     * Initializes the context.
404
     */
405
    useEffect(() => {
406
        refreshAnnotation();
407
    }, [props.annotationId]);
408

    
409
    return (
410
        <AnnotationContext.Provider
411
            value={{
412
                tags,
413
                submitting,
414
                setTags,
415
                addOccurrence,
416
                deleteOccurrence,
417
                finishAnnotation,
418
                changeLength,
419
                changePosition,
420
                changeNote,
421
                changeSentiment,
422
                refreshAnnotation,
423
                annotation,
424
                mappedTags,
425
                markSelectedText,
426
                selectedInstanceId,
427
                setSelectedInstanceId,
428
                selectedOccurrenceId,
429
                setSelectedOccurrenceId,
430
            }}
431
        >
432
            {props.children}
433
        </AnnotationContext.Provider>
434
    );
435
};
436

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