Projekt

Obecné

Profil

Stáhnout (12 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

    
91
/**
92
 * The annotation context that manages active annotations.
93
 */
94
export const AnnotationContext = createContext<IAnnotationContextProvider>({
95
    /**
96
     * Default tags.
97
     */
98
    tags: null,
99

    
100
    /**
101
     * Submitting boolean
102
     */
103
    submitting: false,
104

    
105
    /**
106
     * Default implementation of setTags method.
107
     * @param v Array of new tags.
108
     */
109
    setTags: (v) => {
110
        return;
111
    },
112

    
113
    /**
114
     * Default implementation of addOccurrence method.
115
     * @param tag The tag with new occurrence.
116
     */
117
    addOccurrence: (tag: Tag) => {
118
        return;
119
    },
120

    
121
    /**
122
     * Default implementation of deleteOccurrence method.
123
     * @param occurrence Occurrence that should be deleted.
124
     */
125
    deleteOccurrence: (occurrence: TagInstanceInfo) => {
126
        return;
127
    },
128

    
129
    /**
130
     * Finishes annotation of document (marked as DONE).
131
     */
132
    finishAnnotation: () => {
133
        return;
134
    },
135

    
136
    /**
137
     * Default implementation of changePosition method.
138
     * @param occurrence Occurrence whose position should be changed.
139
     * @param newValue A new position.
140
     */
141
    changePosition: (occurrence: TagInstanceInfo, newValue: number) => {
142
        return;
143
    },
144

    
145
    /**
146
     * Default implementation of changeLength method.
147
     * @param occurrence Occurrence whose length should be changed.
148
     * @param newValue A new length.
149
     */
150
    changeLength: (occurrence: TagInstanceInfo, newValue: number) => {
151
        return;
152
    },
153

    
154
    /**
155
     * Changes a note of an occurrence of an annotation.
156
     * @param occurrence Occurrence whose note should be changed.
157
     * @param newValue New value of the note.
158
     */
159
    changeNote: (occurrence: TagInstanceInfo, newValue: string) => {
160
        return;
161
    },
162

    
163
    /**
164
     * Changes sentiment of an annotation.
165
     * @param occurrence Occurrence whose sentiment should be changed.
166
     * @param newValue New value of the sentiment.
167
     */
168
    changeSentiment: (occurrence: TagInstanceInfo, newValue: ETagSentiment) => {
169
        return;
170
    },
171

    
172
    annotation: null,
173
    mappedTags: null,
174
    refreshAnnotation: () => {
175
        return;
176
    },
177

    
178
    markSelectedText: () => {
179
        return;
180
    },
181
});
182

    
183
/**
184
 * Provider of the annotation context.
185
 * @param props Children that should have access to the annotation context.
186
 * @returns Prepared html of the provider.
187
 */
188
const AnnotationProvider = (props: {
189
    children: React.ReactNode;
190
    annotationId: string;
191
}) => {
192
    const [annotation, setAnnotation] = useState<AnnotationInfo | null>(null);
193

    
194
    /**
195
     * Tags managed by the context.
196
     */
197
    const [tags, setTags] = useState<TagInstanceInfo[] | null>(null);
198

    
199
    const [mappedTags, setMappedTags] = useState<Tag[] | null>(null);
200

    
201
    const [submitting, setSubmitting] = useState(false);
202

    
203
    const router = useRouter();
204

    
205
    async function markSelectedText(
206
        tagId: string,
207
        subtagId: string | null,
208
        instanceId: string | null
209
    ) {
210
        setSubmitting(true);
211
        if (!annotation) {
212
            console.log('annotation not found');
213
            return;
214
        }
215

    
216
        const selectionInfo = GetSelectionInfo(annotation);
217
        if (!selectionInfo) {
218
            console.log(
219
                'not able to continue, selection processing not completed successfully'
220
            );
221
            ShowToast('Není označen žádný text pro přidělení značky', 'warning');
222
            setSubmitting(false);
223
            return;
224
        }
225

    
226
        const id = subtagId ?? tagId;
227
        const type: ETagType = subtagId == null ? ETagType.Tag : ETagType.Subtag;
228
        const res = await annotationController
229
            .annotationAnnotationIdPost(props.annotationId, {
230
                id: id,
231
                instanceId,
232
                type,
233
                position: selectionInfo.startPositionOriginalDocument,
234
                length: selectionInfo.selectionLengthOriginalDocument,
235
                selectedText: selectionInfo.selectedText,
236
            })
237
            .catch((e) =>
238
                ShowToast('Tato část textu je již touto značkou anotována', 'warning')
239
            );
240

    
241
        await refreshAnnotation();
242
    }
243

    
244
    /**
245
     * Default implementation of addOccurrence method.
246
     * @param tag The tag with new occurrence.
247
     */
248
    const addOccurrence = async (tag: Tag) => {
249
        await markSelectedText(tag.tagId, tag.subtagId ?? null, tag.instanceId);
250
    };
251

    
252
    /**
253
     * Deletes an occurrence of an annotation.
254
     * @param occurrence Occurrence that should be deleted.
255
     */
256
    const deleteOccurrence = async (occurrence: TagInstanceInfo) => {
257
        if (!occurrence.occurenceId) {
258
            console.log('invalid occurrence');
259
            return;
260
        }
261

    
262
        ShowConfirmDelete(() => {
263
            annotationController
264
                .annotationAnnotationIdOccurenceIdDelete(
265
                    props.annotationId,
266
                    occurrence.occurenceId ?? ''
267
                )
268
                .then(() => refreshAnnotation());
269
        }, 'značku');
270
    };
271

    
272
    /**
273
     * Finishes annotation of document (marked as DONE).
274
     */
275
    const finishAnnotation = () => {
276
        const req: MarkAnnotationDoneRequest = { done: true };
277
        ShowConfirm(
278
            () =>
279
                annotationController
280
                    .annotationAnnotationIdDonePut(props.annotationId, req)
281
                    .then(() => {
282
                        router.push('/documents/annotator');
283
                        ShowToast('Anotování bylo úspěšně dokončeno');
284
                    }),
285
            'dokončit anotování'
286
        );
287
    };
288

    
289
    /**
290
     * Changes a position of an occurrence of an annotation.
291
     * @param occurrence Occurrence whose position should be changed.
292
     * @param newValue New value of the position.
293
     */
294
    const changePosition = (occurrence: TagInstanceInfo, newValue: number) => {
295
        //TODO: Implement method (should use objects from server API)
296
    };
297

    
298
    /**
299
     * Changes a length of an occurrence of an annotation.
300
     * @param occurrence Occurrence whose length should be changed.
301
     * @param newValue New value of the length.
302
     */
303
    const changeLength = (occurrence: TagInstanceInfo, newValue: number) => {
304
        //TODO: Implement method (should use objects from server API)
305
    };
306

    
307
    /**
308
     * Changes a note of an occurrence of an annotation.
309
     * @param occurrence Occurrence whose note should be changed.
310
     * @param newValue New value of the note.
311
     */
312
    const changeNote = async (occurrence: TagInstanceInfo, newValue: string) => {
313
        if (!occurrence.occurenceId) {
314
            console.log('invalid occurrence');
315
            return;
316
        }
317

    
318
        const postRes =
319
            await annotationController.annotationAnnotationIdTagOccurenceIdNotePost(
320
                props.annotationId,
321
                occurrence.occurenceId,
322
                { note: newValue }
323
            );
324

    
325
        occurrence.note = newValue;
326
    };
327

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

    
342
        const putRes =
343
            await annotationController.annotationAnnotationIdInstanceIdSentimentPut(
344
                props.annotationId,
345
                occurrence.instance,
346
                { sentiment: newValue }
347
            );
348

    
349
        occurrence.sentiment = newValue;
350
    };
351

    
352
    const remapAnnotations = (data: AnnotationInfo) => {
353
        let map = new Map<string, Tag>();
354
        data.tagInstances?.forEach((tagInstance) => {
355
            if (map.has(tagInstance.instance ?? '-')) {
356
                let tag = map.get(tagInstance.instance ?? '-');
357
                tag!.occurrences = [...tag!.occurrences, tagInstance];
358
            } else {
359
                map.set(tagInstance.instance ?? '-', {
360
                    tagName: tagInstance.tagName ?? '',
361
                    subtagName: tagInstance.subTagName ?? null,
362
                    category: tagInstance.tagCategoryName ?? '',
363
                    occurrences: [tagInstance],
364
                    tagId: tagInstance.tagId ?? '',
365
                    instanceId: tagInstance.instance ?? '',
366
                    subtagId: tagInstance.subTagId ?? null,
367
                });
368
            }
369
        });
370

    
371
        setMappedTags(Array.from(map.values()));
372
    };
373

    
374
    async function refreshAnnotation() {
375
        const data = await annotationController.annotationAnnotationIdGet(
376
            props.annotationId
377
        );
378

    
379
        remapAnnotations(data.data);
380
        setAnnotation(data.data ?? null);
381
        setSubmitting(false);
382
    }
383

    
384
    /**
385
     * Initializes the context.
386
     */
387
    useEffect(() => {
388
        refreshAnnotation();
389
    }, [props.annotationId]);
390

    
391
    return (
392
        <AnnotationContext.Provider
393
            value={{
394
                tags,
395
                submitting,
396
                setTags,
397
                addOccurrence,
398
                deleteOccurrence,
399
                finishAnnotation,
400
                changeLength,
401
                changePosition,
402
                changeNote,
403
                changeSentiment,
404
                refreshAnnotation,
405
                annotation,
406
                mappedTags,
407
                markSelectedText,
408
            }}
409
        >
410
            {props.children}
411
        </AnnotationContext.Provider>
412
    );
413
};
414

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