Projekt

Obecné

Profil

Stáhnout (11.9 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
            })
236
            .catch((e) =>
237
                ShowToast('Tato část textu je již touto značkou anotována', 'warning')
238
            );
239

    
240
        await refreshAnnotation();
241
    }
242

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

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

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

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

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

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

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

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

    
324
        occurrence.note = newValue;
325
    };
326

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

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

    
348
        occurrence.sentiment = newValue;
349
    };
350

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

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

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

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

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

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

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