@konfuzio/document-validation-ui 0.1.19-dev.2 → 0.1.19

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (143) hide show
  1. package/.eslintrc.js +10 -10
  2. package/.prettierrc.json +1 -1
  3. package/LICENSE +21 -21
  4. package/README.md +24 -24
  5. package/cypress.config.js +13 -13
  6. package/dist/js/app.js +1 -1
  7. package/dist/js/app.js.map +1 -1
  8. package/dist/js/chunk-vendors.js +1 -1
  9. package/dist/js/chunk-vendors.js.map +1 -1
  10. package/jest.config.js +4 -4
  11. package/package.json +66 -66
  12. package/src/api.js +82 -82
  13. package/src/assets/images/AcceptedCheckMark.vue +8 -8
  14. package/src/assets/images/AcceptedUser.vue +8 -8
  15. package/src/assets/images/ActionIcon.vue +60 -60
  16. package/src/assets/images/ArrowDownKey.vue +11 -11
  17. package/src/assets/images/ArrowUpKey.vue +11 -11
  18. package/src/assets/images/CategoryIconImg.vue +13 -13
  19. package/src/assets/images/CheckMark.vue +8 -8
  20. package/src/assets/images/DraggableIcon.vue +14 -14
  21. package/src/assets/images/EditDocIcon.vue +12 -12
  22. package/src/assets/images/EmptyStateImg.vue +129 -129
  23. package/src/assets/images/ErrorIcon.vue +28 -28
  24. package/src/assets/images/EyeIcon.vue +11 -11
  25. package/src/assets/images/FileNameNotSavedImage.vue +26 -26
  26. package/src/assets/images/FileNameSavedImage.vue +14 -14
  27. package/src/assets/images/FitZoomIcon.vue +16 -16
  28. package/src/assets/images/GridIcon.vue +16 -16
  29. package/src/assets/images/KeyboardIcon.vue +16 -16
  30. package/src/assets/images/MagicWandIcon.vue +16 -16
  31. package/src/assets/images/MinusIcon.vue +13 -13
  32. package/src/assets/images/NotFoundIcon.vue +16 -16
  33. package/src/assets/images/NotOptimizedIllustration.vue +651 -651
  34. package/src/assets/images/PlusIcon.vue +13 -13
  35. package/src/assets/images/QuestionMark.vue +12 -12
  36. package/src/assets/images/ServerImage.vue +73 -73
  37. package/src/assets/images/SettingsIcon.vue +14 -14
  38. package/src/assets/images/SplitLines.vue +18 -18
  39. package/src/assets/images/SplitZigZag.vue +49 -49
  40. package/src/assets/images/StarIcon.vue +16 -16
  41. package/src/assets/images/StatusImg.vue +14 -14
  42. package/src/assets/images/TranslateArrows.vue +33 -33
  43. package/src/assets/scss/ann_set_table_options.scss +26 -26
  44. package/src/assets/scss/annotation_details.scss +141 -141
  45. package/src/assets/scss/choose_label_set_modal.scss +65 -65
  46. package/src/assets/scss/document_action_bar.scss +37 -37
  47. package/src/assets/scss/document_annotations.scss +558 -558
  48. package/src/assets/scss/document_category.scss +85 -85
  49. package/src/assets/scss/document_dashboard.scss +52 -52
  50. package/src/assets/scss/document_edit.scss +410 -410
  51. package/src/assets/scss/document_error.scss +81 -81
  52. package/src/assets/scss/document_name.scss +60 -60
  53. package/src/assets/scss/document_page.scss +12 -12
  54. package/src/assets/scss/document_thumbnails.scss +41 -41
  55. package/src/assets/scss/document_toolbar.scss +111 -111
  56. package/src/assets/scss/document_top_bar.scss +171 -171
  57. package/src/assets/scss/document_viewport_modal.scss +25 -25
  58. package/src/assets/scss/documents_list.scss +141 -141
  59. package/src/assets/scss/edit_page_thumbnail.scss +53 -53
  60. package/src/assets/scss/empty_state.scss +34 -34
  61. package/src/assets/scss/extracting_data.scss +35 -35
  62. package/src/assets/scss/imports.scss +1 -1
  63. package/src/assets/scss/multi_ann_table_overlay.scss +38 -38
  64. package/src/assets/scss/multi_ann_table_popup.scss +12 -12
  65. package/src/assets/scss/new_annotation.scss +102 -102
  66. package/src/assets/scss/scrolling_document.scss +19 -19
  67. package/src/assets/scss/theme.scss +801 -801
  68. package/src/assets/scss/variables.scss +66 -66
  69. package/src/components/App.cy.js +7 -7
  70. package/src/components/App.vue +187 -187
  71. package/src/components/DocumentAnnotations/AnnotationActionButtons.vue +152 -152
  72. package/src/components/DocumentAnnotations/AnnotationContent.vue +210 -210
  73. package/src/components/DocumentAnnotations/AnnotationDetails.vue +251 -251
  74. package/src/components/DocumentAnnotations/AnnotationRow.vue +752 -752
  75. package/src/components/DocumentAnnotations/AnnotationSetActionButtons.vue +89 -89
  76. package/src/components/DocumentAnnotations/ChooseLabelSetModal.vue +186 -186
  77. package/src/components/DocumentAnnotations/DocumentAnnotations.cy.js +441 -441
  78. package/src/components/DocumentAnnotations/DocumentAnnotations.vue +534 -534
  79. package/src/components/DocumentAnnotations/DocumentLabel.vue +189 -189
  80. package/src/components/DocumentAnnotations/EmptyAnnotation.vue +193 -193
  81. package/src/components/DocumentAnnotations/EmptyState.vue +21 -21
  82. package/src/components/DocumentAnnotations/ExtractingData.vue +41 -41
  83. package/src/components/DocumentAnnotations/LoadingAnnotations.vue +43 -43
  84. package/src/components/DocumentAnnotations/LoadingLabels.vue +43 -43
  85. package/src/components/DocumentAnnotations/MultiAnnotationTableOverlay.vue +338 -338
  86. package/src/components/DocumentAnnotations/index.js +8 -8
  87. package/src/components/DocumentCategory.vue +281 -281
  88. package/src/components/DocumentDashboard.vue +170 -170
  89. package/src/components/DocumentEdit/DocumentEdit.cy.js +541 -541
  90. package/src/components/DocumentEdit/DocumentEdit.vue +503 -503
  91. package/src/components/DocumentEdit/EditConfirmationModal.vue +55 -55
  92. package/src/components/DocumentEdit/EditPageThumbnail.vue +114 -114
  93. package/src/components/DocumentEdit/EditPages.vue +161 -161
  94. package/src/components/DocumentEdit/EditSidebar.vue +154 -154
  95. package/src/components/DocumentEdit/RenameAndCategorize.vue +184 -184
  96. package/src/components/DocumentEdit/SidebarButtons.vue +53 -53
  97. package/src/components/DocumentEdit/SplitInfoBar.vue +21 -21
  98. package/src/components/DocumentEdit/index.js +4 -4
  99. package/src/components/DocumentModals/DocumentErrorModal.vue +58 -58
  100. package/src/components/DocumentModals/NotOptimizedViewportModal.vue +51 -51
  101. package/src/components/DocumentPage/ActionBar.vue +48 -48
  102. package/src/components/DocumentPage/AnnSetTableOptions.vue +111 -111
  103. package/src/components/DocumentPage/BoxSelection.vue +152 -152
  104. package/src/components/DocumentPage/DocumentPage.cy.js +92 -92
  105. package/src/components/DocumentPage/DocumentPage.vue +568 -568
  106. package/src/components/DocumentPage/DocumentToolbar.cy.js +215 -215
  107. package/src/components/DocumentPage/DocumentToolbar.vue +228 -228
  108. package/src/components/DocumentPage/DummyPage.vue +55 -55
  109. package/src/components/DocumentPage/MultiAnnSelection.vue +371 -371
  110. package/src/components/DocumentPage/NewAnnotation.vue +308 -308
  111. package/src/components/DocumentPage/ScrollingDocument.vue +149 -149
  112. package/src/components/DocumentPage/ScrollingPage.vue +179 -179
  113. package/src/components/DocumentPage/index.js +5 -5
  114. package/src/components/DocumentThumbnails/DocumentThumbnails.cy.js +67 -67
  115. package/src/components/DocumentThumbnails/DocumentThumbnails.vue +132 -132
  116. package/src/components/DocumentThumbnails/LoadingThumbnail.vue +25 -25
  117. package/src/components/DocumentThumbnails/index.js +1 -1
  118. package/src/components/DocumentTopBar/DocumentName.vue +236 -236
  119. package/src/components/DocumentTopBar/DocumentTopBar.cy.js +222 -222
  120. package/src/components/DocumentTopBar/DocumentTopBar.vue +202 -202
  121. package/src/components/DocumentTopBar/DocumentTopBarButtons.vue +183 -183
  122. package/src/components/DocumentTopBar/KeyboardActionsDescription.vue +74 -74
  123. package/src/components/DocumentTopBar/index.js +3 -3
  124. package/src/components/DocumentsList/DocumentsList.vue +121 -121
  125. package/src/components/DocumentsList/index.js +1 -1
  126. package/src/components/ErrorMessage.vue +40 -40
  127. package/src/components/index.js +1 -1
  128. package/src/constants.js +5 -5
  129. package/src/directives/scroll.js +28 -28
  130. package/src/i18n.js +22 -22
  131. package/src/icons.js +45 -45
  132. package/src/locales/de.json +148 -148
  133. package/src/locales/en.json +148 -148
  134. package/src/main.js +26 -26
  135. package/src/store/category.js +191 -191
  136. package/src/store/display.js +311 -311
  137. package/src/store/document.js +1438 -1438
  138. package/src/store/edit.js +316 -316
  139. package/src/store/index.js +21 -21
  140. package/src/store/project.js +143 -143
  141. package/src/store/selection.js +210 -210
  142. package/src/utils/utils.js +54 -54
  143. package/vue.config.js +25 -25
@@ -1,1438 +1,1438 @@
1
- import myImports from "../api";
2
- import { MULTI_ANN_TABLE_FEATURE } from "../constants";
3
- import {
4
- sleep,
5
- getURLQueryParam,
6
- navigateToNewDocumentURL,
7
- getURLPath,
8
- } from "../utils/utils";
9
-
10
- const HTTP = myImports.HTTP;
11
- const documentPollDuration = 1000;
12
- export const table_reference_api = "api.v3.dvui.table";
13
-
14
- const state = {
15
- loading: true,
16
- pages: [],
17
- annotationSets: null,
18
- annotations: null,
19
- labels: [],
20
- documentId: process.env.VUE_APP_DOCUMENT_ID,
21
- sidebarAnnotationSelected: null,
22
- documentAnnotationSelected: null,
23
- selectedDocument: null,
24
- recalculatingAnnotations: false,
25
- editAnnotation: null,
26
- missingAnnotations: [],
27
- publicView: true,
28
- showActionError: false,
29
- errorMessage: null,
30
- showDocumentError: false,
31
- annotationsMarkedAsMissing: null,
32
- errorMessageWidth: null,
33
- hoveredAnnotationSet: null,
34
- newAcceptedAnnotations: null,
35
- serverError: false,
36
- splittingSuggestions: null,
37
- enableGroupingFeature: true,
38
- };
39
-
40
- const getters = {
41
- /**
42
- * Get entities inside a box
43
- */
44
- entitiesOnSelection: (state) => (box, page) => {
45
- return page.entities.filter(
46
- (entity) =>
47
- box.x0 <= entity.x0 &&
48
- box.x1 >= entity.x1 &&
49
- box.y0 <= entity.y0 &&
50
- box.y1 >= entity.y1
51
- );
52
- },
53
-
54
- /**
55
- * Number of pages. If the pages array doesn't exist yet, return 0.
56
- */
57
- pageCount: (state) => {
58
- if (state.selectedDocument.pages) {
59
- return state.selectedDocument.pages.length;
60
- }
61
- return 0;
62
- },
63
-
64
- /**
65
- * Returns the current page
66
- */
67
- pageSelected: (state, _, rootState) => {
68
- if (state.pages) {
69
- return state.pages[rootState.display.currentPage - 1];
70
- }
71
- return null;
72
- },
73
-
74
- /**
75
- * Returns a page in the given index
76
- */
77
- pageAtIndex: (state) => (index) => {
78
- if (state.selectedDocument && state.selectedDocument.pages) {
79
- return state.selectedDocument.pages[index];
80
- }
81
- return null;
82
- },
83
-
84
- /**
85
- * Checks if is to scroll to an annotation in the document
86
- */
87
- scrollDocumentToAnnotation: (state) => {
88
- return (
89
- state.documentAnnotationSelected &&
90
- state.documentAnnotationSelected.scrollTo
91
- );
92
- },
93
-
94
- /**
95
- * Checks if the document is categorized and ready to start the review
96
- */
97
- categorizationIsConfirmed: (state) => {
98
- if (state.selectedDocument) {
99
- if (
100
- state.selectedDocument.category_is_revised ||
101
- state.selectedDocument.is_reviewed
102
- ) {
103
- return true;
104
- } else if (!state.selectedDocument.category) {
105
- return false;
106
- } else {
107
- // check if there's any annotation already approved
108
- const found = state.annotations.find((annotation) => {
109
- return annotation.revised;
110
- });
111
- return found != undefined;
112
- }
113
- }
114
- return false;
115
- },
116
-
117
- /**
118
- * Gets labels for an annotation creation from a label/annotation set
119
- */
120
- labelsFilteredForAnnotationCreation: (_, getters) => (set) => {
121
- let availableLabels = [];
122
- if (set.id && set.labels) {
123
- // check if label can be multiple, if there's already an annotation created & if it's a negative annotation
124
- set.labels.map((label) => {
125
- // check if we already added the same label to the array
126
- const found = availableLabels.find((l) => l.id === label.id);
127
-
128
- if (found) return;
129
-
130
- if (label.annotations.length === 0) {
131
- availableLabels.push(label);
132
- } else {
133
- if (label.has_multiple_top_candidates) {
134
- availableLabels.push(label);
135
- } else {
136
- // if the label has negative annotations, we show the label
137
- label.annotations.map((annotation) => {
138
- if (getters.isNegative(annotation)) {
139
- availableLabels.push(label);
140
- }
141
- });
142
- }
143
- }
144
- });
145
- } else if (set.labels) {
146
- // if not existing ann set, then return all labels
147
- availableLabels = set.labels;
148
- }
149
- return availableLabels;
150
- },
151
-
152
- /* Checks if annotation is in deleted state */
153
- isAnnotationDeleted: (state) => (annotation) => {
154
- if (annotation) {
155
- return annotation.revised && !annotation.is_correct;
156
- }
157
- return false;
158
- },
159
-
160
- /* Checks if the label has annotations to show */
161
- labelHasAnnotations: (_, getters) => (label) => {
162
- const annotations = label.annotations.filter((annotation) => {
163
- return !getters.isAnnotationDeleted(annotation);
164
- });
165
- return annotations.length > 0;
166
- },
167
-
168
- /* Checks if the document has an annotation set */
169
- annotationSetExists: (state) => (annotationSetId) => {
170
- return state.annotationSets.find((annSet) => annSet.id === annotationSetId);
171
- },
172
-
173
- /* Get annotation sets created in table */
174
- annotationSetsInTable: (state) => () => {
175
- const annotationSetsList = {};
176
- if (MULTI_ANN_TABLE_FEATURE) {
177
- state.annotationSets.forEach((annotationSet) => {
178
- let addAnnotationSet = false;
179
- if (annotationSet.labels) {
180
- annotationSet.labels.forEach((label) => {
181
- if (
182
- label.annotations &&
183
- label.annotations.find(
184
- (annotation) =>
185
- annotation.origin && annotation.origin === table_reference_api
186
- )
187
- ) {
188
- addAnnotationSet = true;
189
- return;
190
- }
191
- });
192
- }
193
- if (addAnnotationSet) {
194
- // group by label set
195
- if (annotationSetsList[`${annotationSet.label_set.id}`]) {
196
- annotationSetsList[`${annotationSet.label_set.id}`].push(
197
- annotationSet
198
- );
199
- } else {
200
- annotationSetsList[`${annotationSet.label_set.id}`] = [
201
- annotationSet,
202
- ];
203
- }
204
- }
205
- });
206
- }
207
- return annotationSetsList;
208
- },
209
-
210
- /* Get annotation sets without tables */
211
- annotationSetsToShowInList: (state) => () => {
212
- const annotationSetsList = [];
213
- state.annotationSets.forEach((annotationSet) => {
214
- let addAnnotationSet = true;
215
- if (annotationSet.labels) {
216
- annotationSet.labels.forEach((label) => {
217
- if (
218
- MULTI_ANN_TABLE_FEATURE &&
219
- label.annotations &&
220
- label.annotations.find(
221
- (annotation) =>
222
- annotation.origin && annotation.origin === table_reference_api
223
- )
224
- ) {
225
- addAnnotationSet = false;
226
- return;
227
- }
228
- });
229
- }
230
- if (addAnnotationSet) {
231
- annotationSetsList.push(annotationSet);
232
- }
233
- });
234
- return annotationSetsList;
235
- },
236
-
237
- /* Get annotations inside a list of annotation sets */
238
- annotationsInAnnotationsSets: (state) => (annotationsSets) => {
239
- const annotations = [];
240
- annotationsSets.forEach((annotationSet) => {
241
- annotationSet.labels.forEach((label) => {
242
- label.annotations.forEach((annotation) => {
243
- annotations.push(annotation);
244
- });
245
- });
246
- });
247
- return annotations;
248
- },
249
-
250
- /* Process annotations and extract labels and sets */
251
- processAnnotationSets: (state, getters) => (annotationSets) => {
252
- // group annotations for sidebar
253
- const annotations = [];
254
- const labels = [];
255
- const processedAnnotationSets = annotationSets.map((annotationSet) => {
256
- const annotationSetLabels = annotationSet.labels.map((label) => {
257
- // add annotations to the document array
258
- annotations.push(...label.annotations);
259
- labels.push(label);
260
- // add labels to the labels array
261
- return label;
262
- });
263
- annotationSet.labels = annotationSetLabels;
264
- return annotationSet;
265
- });
266
-
267
- return {
268
- annotationSets: processedAnnotationSets,
269
- labels,
270
- annotations,
271
- };
272
- },
273
-
274
- /* Checks if there are annotations correct in the document */
275
- documentHasCorrectAnnotations: (state) => {
276
- if (
277
- !state.annotations ||
278
- (state.annotations &&
279
- state.annotations.filter((ann) => ann.is_correct).length > 0)
280
- ) {
281
- return true;
282
- }
283
- return false;
284
- },
285
-
286
- /* Returns the number of accepted annotations in a label */
287
- numberOfAcceptedAnnotationsInLabel: (_) => (label) => {
288
- const annotations = label.annotations.filter((annotation) => {
289
- return annotation.revised && annotation.is_correct;
290
- });
291
- return annotations.length;
292
- },
293
-
294
- /**
295
- * Checks if theres a group of annotation sets and add an index number to them
296
- */
297
- numberOfAnnotationSetGroup: (state) => (annotationSet) => {
298
- let found = false;
299
- let value = 0;
300
- let index = 0;
301
- if (state.annotationSets) {
302
- state.annotationSets.map((annotationSetTemp) => {
303
- if (
304
- annotationSetTemp.id !== annotationSet.id &&
305
- annotationSetTemp.label_set.id === annotationSet.label_set.id &&
306
- annotationSetTemp.label_set.name === annotationSet.label_set.name
307
- ) {
308
- found = true;
309
- index++;
310
- } else if (
311
- annotationSetTemp.label_set.id === annotationSet.label_set.id
312
- ) {
313
- value = index;
314
- }
315
- });
316
- return found ? `${value + 1}` : "";
317
- }
318
- return "";
319
- },
320
-
321
- /**
322
- * Get label with annotations filtered if the label supports multiple or not
323
- */
324
- annotationsInLabelFiltered: (state) => (label) => {
325
- let labelToReturn;
326
- if (
327
- label.has_multiple_top_candidates === false &&
328
- label.annotations &&
329
- label.annotations.length > 1
330
- ) {
331
- let highestConfidenceAnnotation = label.annotations[0];
332
- for (let i = 1; i < label.annotations.length; i++) {
333
- // check which one has more confidence or if it's the same, then check if one is revised or not
334
- if (
335
- highestConfidenceAnnotation.confidence <
336
- label.annotations[i].confidence ||
337
- (highestConfidenceAnnotation.confidence ===
338
- label.annotations[i].confidence &&
339
- label.annotations[i].revised)
340
- ) {
341
- highestConfidenceAnnotation = label.annotations[i];
342
- }
343
- }
344
- labelToReturn = {
345
- ...label,
346
- annotations: [highestConfidenceAnnotation],
347
- };
348
- } else {
349
- labelToReturn = {
350
- ...label,
351
- };
352
- }
353
- return labelToReturn;
354
- },
355
-
356
- /**
357
- * Checks if annotation is being edited
358
- */
359
- isAnnotationInEditMode:
360
- (state) =>
361
- (annotationId, index = null) => {
362
- if (state.editAnnotation && annotationId) {
363
- if (index != null) {
364
- return (
365
- state.editAnnotation.id === annotationId &&
366
- state.editAnnotation.index === index
367
- );
368
- }
369
- return state.editAnnotation.id === annotationId;
370
- }
371
- },
372
-
373
- /**
374
- * Get number of empty labels per annotation set
375
- */
376
- emptyLabels: (state, getters) => (annotationSet) => {
377
- const pendingEmpty = [];
378
-
379
- annotationSet.labels.map((label) => {
380
- const foundMissing = state.missingAnnotations.find(
381
- (l) =>
382
- l.label === label.id &&
383
- annotationSet.id === l.annotation_set &&
384
- annotationSet.label_set.id === l.label_set
385
- );
386
-
387
- const foundNegative = label.annotations.find((annotation) =>
388
- getters.isNegative(annotation)
389
- );
390
-
391
- if (!foundMissing && (label.annotations.length === 0 || foundNegative)) {
392
- pendingEmpty.push(label);
393
- }
394
- });
395
-
396
- return pendingEmpty;
397
- },
398
-
399
- annotationIsNotFound: (state) => (annotationSet, label) => {
400
- // Check if the combined label and label set have been marked as missing
401
- // or if the document is in public mode
402
- if (state.missingAnnotations.length === 0) {
403
- return false;
404
- } else {
405
- const found = state.missingAnnotations.filter(
406
- (el) =>
407
- el.label === label.id &&
408
- el.annotation_set === annotationSet.id &&
409
- el.label_set === annotationSet.label_set.id
410
- );
411
- return found.length !== 0;
412
- }
413
- },
414
-
415
- // Check if document is ready to be finished
416
- isDocumentReadyToFinishReview: (state) => {
417
- // check if all annotations are correct
418
- let notCorrect;
419
-
420
- const emptyAnnotations = [];
421
-
422
- if (state.labels.length === 0) return false;
423
-
424
- if (state.annotationSets && state.annotationSets.length > 0) {
425
- state.annotationSets.forEach((annSet) => {
426
- annSet.labels.map((label) => {
427
- // return only labels with empty annotations
428
- if (label.annotations.length === 0) {
429
- emptyAnnotations.push({
430
- label: label.id,
431
- label_set: annSet.label_set.id,
432
- });
433
- }
434
- });
435
- });
436
- }
437
-
438
- if (state.annotations) {
439
- notCorrect = state.annotations.filter((a) => !a.is_correct);
440
-
441
- if (notCorrect) {
442
- notCorrect.map((annotation) => {
443
- emptyAnnotations.push(annotation);
444
- });
445
- }
446
- }
447
-
448
- // if all annotations are correct
449
- // and if there are no empty annotations or
450
- // all empty annotations or negative annotations were marked as missing,
451
- // we can finish the review
452
- if (
453
- !emptyAnnotations ||
454
- !state.missingAnnotations ||
455
- state.missingAnnotations.length === emptyAnnotations.length
456
- ) {
457
- return true;
458
- }
459
- return false;
460
- },
461
-
462
- isDocumentReviewed: (state) => {
463
- return state.selectedDocument.is_reviewed;
464
- },
465
-
466
- /**
467
- * Get number of annotations pending review per annotation set
468
- */
469
- notCorrectAnnotations: () => (annotationSet) => {
470
- const labels = annotationSet.labels.filter(
471
- (label) => label.annotations.length > 0
472
- );
473
-
474
- const annotationsWithPendingReview = [];
475
-
476
- labels.map((label) => {
477
- const foundPendingAnnotations = label.annotations.filter(
478
- (ann) => !ann.is_correct && !ann.revised
479
- );
480
-
481
- if (foundPendingAnnotations && foundPendingAnnotations.length > 0) {
482
- foundPendingAnnotations.map((annotation) => {
483
- annotationsWithPendingReview.push(annotation);
484
- });
485
- }
486
-
487
- // Check if we have grouped annotations by same label
488
- if (state.enableGroupingFeature && label.annotations.length < 2) {
489
- return annotationsWithPendingReview.length - label.annotations.length;
490
- }
491
- });
492
-
493
- return annotationsWithPendingReview;
494
- },
495
-
496
- /**
497
- * Check if the document was extracted correctly and is ready to be reviewed
498
- */
499
- isDocumentReadyToBeReviewed: () => (document) => {
500
- return document.status_data === 2 && document.labeling_available === 1;
501
- },
502
-
503
- /**
504
- * Check if the document had an error during extraction
505
- */
506
- documentHadErrorDuringExtraction: () => (document) => {
507
- return document.status_data === 111;
508
- },
509
-
510
- /**
511
- * check if the document has a dataset status of 'Training', 'Test' or 'Preparation'
512
- * or if it is Ready Only / Reviewed
513
- * and if so disable the option to edit the document
514
- */
515
- documentCannotBeEdited:
516
- (state) =>
517
- (document = state.selectedDocument) => {
518
- return (
519
- document.dataset_status === 1 ||
520
- document.dataset_status === 2 ||
521
- document.dataset_status === 3 ||
522
- document.is_reviewed
523
- );
524
- },
525
-
526
- /**
527
- * If automatic splitting is enabled for the project
528
- */
529
- waitingForSplittingConfirmation: () => (document) => {
530
- return document && document.status_data === 41;
531
- },
532
-
533
- /**
534
- * Show the Smart Split switch or not
535
- */
536
- documentHasProposedSplit: () => (document) => {
537
- return document.proposed_split && document.proposed_split.length > 0;
538
- },
539
-
540
- /**
541
- * Check the level of confidence of an annotation
542
- */
543
- confidence: () => (annotation) => {
544
- if (annotation) {
545
- return annotation.confidence;
546
- } else {
547
- return null;
548
- }
549
- },
550
-
551
- /**
552
- * Check status of annotation
553
- */
554
- notExtracted: () => (annotation) => {
555
- if (annotation) {
556
- return !annotation.span;
557
- } else {
558
- return true;
559
- }
560
- },
561
- created: () => (annotation) => {
562
- if (annotation) {
563
- return (
564
- annotation.created_by && !annotation.revised && annotation.is_correct
565
- );
566
- } else {
567
- return null;
568
- }
569
- },
570
- edited: () => (annotation) => {
571
- if (annotation) {
572
- if (annotation.offset_string !== annotation.offset_string_original) {
573
- return true;
574
- } else if (annotation.created_by) {
575
- return true;
576
- } else {
577
- return false;
578
- }
579
- } else {
580
- return null;
581
- }
582
- },
583
- accepted: () => (annotation) => {
584
- if (annotation) {
585
- return annotation.revised && annotation.is_correct;
586
- } else {
587
- return null;
588
- }
589
- },
590
- isNegative: () => (annotation) => {
591
- if (annotation) {
592
- return !annotation.is_correct && annotation.revised;
593
- } else {
594
- return null;
595
- }
596
- },
597
-
598
- /**
599
- * Check for user who created or revised the annotation
600
- */
601
- getUser: () => (annotation) => {
602
- if (annotation) {
603
- if (annotation.created_by && !annotation.revised) {
604
- // If the annotation was created but not yet revised
605
- // we show who created it
606
- return annotation.created_by;
607
- } else if (annotation.revised && annotation.revised_by) {
608
- return annotation.revised_by;
609
- } else {
610
- // If both revised_by and created_by are null, we don't show any user
611
- return null;
612
- }
613
- } else {
614
- return null;
615
- }
616
- },
617
-
618
- /**
619
- * Check if there is just one annotation set from a label set
620
- */
621
- isOnlyMultipleAnnotationSet: (state) => (annotationSet) => {
622
- const sameSets = state.annotationSets.filter(set => set.label_set.id === annotationSet.label_set.id);
623
-
624
- return sameSets.length === 1 ? true : false;
625
- },
626
-
627
- /**
628
- * Check if the annotation set can appear multiple times
629
- */
630
- annotationSetCanBeMultiple: (_) => (annotationSet) => {
631
- return annotationSet.label_set.has_multiple_annotation_sets;
632
- },
633
-
634
- /**
635
- * Check if the annotation set has only empty labels
636
- */
637
- annotationSetHasNoFilledLabels: (_) => (annotationSet) => {
638
- const annotations = annotationSet.labels.flatMap(label => {
639
- return label.annotations;
640
- });
641
-
642
- console.log(annotations);
643
-
644
- return annotations.length === 0 ? true : false;
645
- },
646
- };
647
-
648
- const actions = {
649
- startLoading: ({ commit }) => {
650
- commit("SET_LOADING", true);
651
- },
652
- endLoading: ({ commit }) => {
653
- commit("SET_LOADING", false);
654
- },
655
- setDocId: ({ commit }, id) => {
656
- commit("SET_PAGES", []);
657
- commit("SET_DOC_ID", id);
658
- },
659
- setSidebarAnnotationSelected: ({ commit }, annotation) => {
660
- commit("SET_ANNOTATION_SELECTED", annotation);
661
- },
662
- setAnnotationSets: ({ commit }, annotationSets) => {
663
- commit("SET_ANNOTATION_SETS", annotationSets);
664
- },
665
- setEditAnnotation: (
666
- { commit },
667
- { id, index, label, labelSet, annotationSet }
668
- ) => {
669
- const value = {
670
- id,
671
- index,
672
- label,
673
- labelSet,
674
- annotationSet,
675
- };
676
- commit("SET_EDIT_ANNOTATION", value);
677
- },
678
- resetEditAnnotation: ({ commit }) => {
679
- commit("RESET_EDIT_ANNOTATION");
680
- },
681
- setAnnotations: ({ commit }, annotations) => {
682
- commit("SET_ANNOTATIONS", annotations);
683
- },
684
- setLabels: ({ commit }, labels) => {
685
- commit("SET_LABELS", labels);
686
- },
687
- setPages: ({ commit }, pages) => {
688
- commit("SET_PAGES", pages);
689
- },
690
- setSelectedDocument: ({ commit }, document) => {
691
- commit("SET_SELECTED_DOCUMENT", document);
692
- },
693
- setPublicView: ({ commit }, publicView) => {
694
- commit("SET_PUBLIC_VIEW", publicView);
695
- },
696
- startRecalculatingAnnotations: ({ commit }) => {
697
- commit("SET_RECALCULATING_ANNOTATIONS", true);
698
- },
699
- endRecalculatingAnnotations: ({ commit }) => {
700
- commit("SET_RECALCULATING_ANNOTATIONS", false);
701
- },
702
- setMissingAnnotations: ({ commit }, missingAnnotations) => {
703
- commit("SET_MISSING_ANNOTATIONS", missingAnnotations);
704
- },
705
- setErrorMessage: ({ commit, dispatch }, message) => {
706
- if (message) {
707
- commit("SET_SHOW_ACTION_ERROR", true);
708
- } else {
709
- commit("SET_SHOW_ACTION_ERROR", false);
710
- }
711
-
712
- commit("SET_ERROR_MESSAGE", message);
713
-
714
- dispatch("closeErrorMessage");
715
- },
716
- setDocumentError: ({ commit }, value) => {
717
- commit("SET_DOCUMENT_ERROR", value);
718
- },
719
- setAnnotationsMarkedAsMissing: ({ commit }, annotations) => {
720
- commit("SET_ANNOTATIONS_MARKED_AS_MISSING", annotations);
721
- },
722
- setErrorMessageWidth: ({ commit }, width) => {
723
- commit("SET_ERROR_MESSAGE_WIDTH", width);
724
- },
725
- setHoveredAnnotationSet: ({ commit }, annotationSet) => {
726
- commit("SET_HOVERED_ANNOTATION_SET", annotationSet);
727
- },
728
- setNewAcceptedAnnotations: ({ commit }, annotations) => {
729
- commit("SET_NEW_ACCEPTED_ANNOTATIONS", annotations);
730
- },
731
- setSplittingSuggestions: ({ commit }, value) => {
732
- commit("SET_SPLITTING_SUGGESTIONS", value);
733
- },
734
-
735
- /**
736
- * Actions that use HTTP requests always return the axios promise,
737
- * so they can be `await`ed (useful to set the `loading` status).
738
- */
739
- fetchDocument: async (
740
- { commit, state, dispatch, rootState, getters },
741
- pollDocumentList = false
742
- ) => {
743
- let projectId = null;
744
- let categoryId = null;
745
- let isRecalculatingAnnotations = false;
746
-
747
- const initialPage = 1;
748
-
749
- dispatch("startLoading");
750
- dispatch("display/updateCurrentPage", initialPage, {
751
- root: true,
752
- });
753
-
754
- await HTTP.get(`documents/${state.documentId}/`)
755
- .then(async (response) => {
756
- if (response.data) {
757
- const { labels, annotations, annotationSets } =
758
- getters.processAnnotationSets(response.data.annotation_sets);
759
-
760
- // load first page
761
- if (response.data.pages.length > 0) {
762
- dispatch("fetchDocumentPage", initialPage);
763
- }
764
-
765
- // set information on the store
766
- commit("SET_ANNOTATION_SETS", annotationSets);
767
- commit("SET_ANNOTATIONS", annotations);
768
- commit("SET_LABELS", labels);
769
- commit("SET_SELECTED_DOCUMENT", response.data);
770
-
771
- if (response.data.project) {
772
- projectId = response.data.project;
773
-
774
- dispatch("project/setProjectId", projectId, {
775
- root: true,
776
- });
777
- dispatch("project/fetchProjectDetails", projectId, {
778
- root: true,
779
- });
780
- }
781
-
782
- if (getters.documentHasProposedSplit(response.data)) {
783
- commit("SET_SPLITTING_SUGGESTIONS", response.data.proposed_split);
784
- }
785
-
786
- categoryId = response.data.category;
787
- // TODO: add this validation to a method
788
- isRecalculatingAnnotations = response.data.labeling_available !== 1;
789
- }
790
- })
791
- .catch((error) => {
792
- console.log(error, "Could not fetch document details from the backend");
793
- return;
794
- });
795
-
796
- if (!state.publicView) {
797
- await dispatch("fetchMissingAnnotations");
798
-
799
- await dispatch("project/fetchCurrentUser", null, {
800
- root: true,
801
- });
802
-
803
- // Check if we first open the document dashboard or the edit mode
804
- if (
805
- !state.selectedDocument.category ||
806
- (!state.selectedDocument.category_is_revised &&
807
- !getters.documentHasCorrectAnnotations &&
808
- state.missingAnnotations.length === 0)
809
- ) {
810
- dispatch("edit/enableEditMode", null, {
811
- root: true,
812
- });
813
- dispatch("edit/setRenameAndCategorize", true, { root: true });
814
- }
815
-
816
- if (projectId) {
817
- await dispatch("category/fetchCategories", projectId, {
818
- root: true,
819
- });
820
-
821
- // get list of documents not reviewed
822
- await dispatch("project/fetchDocumentList", "is_reviewed=false", {
823
- root: true,
824
- });
825
- }
826
- if (categoryId && rootState.category.createAvailableListOfDocuments) {
827
- await dispatch(
828
- "category/createAvailableDocumentsList",
829
- {
830
- categoryId,
831
- user: rootState.project.currentUser.username,
832
- poll: pollDocumentList,
833
- },
834
- {
835
- root: true,
836
- }
837
- );
838
- }
839
- }
840
- if (isRecalculatingAnnotations) {
841
- commit("SET_RECALCULATING_ANNOTATIONS", true);
842
- dispatch("pollDocumentEndpoint");
843
- }
844
- dispatch("endLoading");
845
- },
846
-
847
- // Get document page data
848
- fetchDocumentPage: ({ commit, state }, page) => {
849
- return HTTP.get(`documents/${state.documentId}/pages/${page}/`)
850
- .then((response) => {
851
- commit("ADD_PAGE", response.data);
852
- })
853
- .catch((error) => {
854
- console.log(error);
855
- });
856
- },
857
-
858
- setDocumentAnnotationSelected: (
859
- { commit },
860
- { annotation, label, span, scrollTo = false }
861
- ) => {
862
- const value = {
863
- scrollTo,
864
- id: annotation.id,
865
- span,
866
- page: span.page_index + 1,
867
- labelName: label ? label.name : "",
868
- };
869
- commit("SET_DOCUMENT_ANNOTATION_SELECTED", value);
870
- },
871
-
872
- scrollToDocumentAnnotationSelected: ({ commit }) => {
873
- commit("SET_DOCUMENT_ANNOTATION_SCROLL", true);
874
- },
875
-
876
- disableDocumentAnnotationSelected: ({ commit }) => {
877
- commit("SET_DOCUMENT_ANNOTATION_SELECTED", null);
878
- },
879
-
880
- createAnnotation: (
881
- { commit, getters, dispatch },
882
- { annotation, negativeAnnotationId }
883
- ) => {
884
- return new Promise((resolve, reject) => {
885
- HTTP.post(`/annotations/`, annotation)
886
- .then(async (response) => {
887
- if (response.status === 201) {
888
- await dispatch("fetchMissingAnnotations");
889
-
890
- if (!getters.annotationSetExists(response.data.annotation_set)) {
891
- const documentData = await dispatch("fetchDocumentData");
892
- if (documentData && documentData.annotation_sets) {
893
- const { labels, annotations, annotationSets } =
894
- getters.processAnnotationSets(documentData.annotation_sets);
895
- commit("SET_ANNOTATION_SETS", annotationSets);
896
- commit("SET_ANNOTATIONS", annotations);
897
- commit("SET_LABELS", labels);
898
- }
899
- } else {
900
- commit("ADD_ANNOTATION", response.data);
901
- if (negativeAnnotationId) {
902
- commit("DELETE_ANNOTATION", negativeAnnotationId);
903
- }
904
- }
905
-
906
- resolve(response);
907
- }
908
- })
909
- .catch((error) => {
910
- reject(error.response);
911
- console.log(error);
912
- });
913
- });
914
- },
915
-
916
- updateAnnotation: (
917
- { commit, getters, dispatch },
918
- { updatedValues, annotationId }
919
- ) => {
920
- commit("SET_NEW_ACCEPTED_ANNOTATIONS", [annotationId]);
921
-
922
- return new Promise((resolve, reject) => {
923
- HTTP.patch(`/annotations/${annotationId}/`, updatedValues)
924
- .then(async (response) => {
925
- if (response.status === 200) {
926
- commit("UPDATE_ANNOTATION", response.data);
927
- commit("SET_NEW_ACCEPTED_ANNOTATIONS", null);
928
-
929
- resolve(true);
930
- }
931
- })
932
- .catch((error) => {
933
- reject(error.response);
934
- console.log(error);
935
- });
936
- });
937
- },
938
-
939
- deleteAnnotation: ({ commit, getters }, { annotationId, annotationSet }) => {
940
- return new Promise((resolve, reject) => {
941
- HTTP.delete(`/annotations/${annotationId}/`)
942
- .then(async (response) => {
943
- if (response.status === 204) {
944
- commit("DELETE_ANNOTATION", annotationId);
945
-
946
- // Check if the deleted annotation was the last one in a multiple annotation set
947
- // and if the annotation set has no annotations
948
- if (annotationSet && getters.annotationSetCanBeMultiple(annotationSet) && getters.annotationSetHasNoFilledLabels(annotationSet)) {
949
- // Check if there is still 1 or more multiple annotation sets for the same label set
950
- if (getters.isOnlyMultipleAnnotationSet(annotationSet)) {
951
- commit("UPDATE_ANNOTATION_SET", annotationSet);
952
- } else {
953
- commit("DELETE_ANNOTATION_SET", annotationSet);
954
- }
955
- }
956
-
957
- resolve(true);
958
- }
959
- })
960
- .catch((error) => {
961
- reject(error.response);
962
- console.log(error);
963
- });
964
- });
965
- },
966
-
967
- updateDocument: ({ commit, state, getters, dispatch }, updatedDocument) => {
968
- return new Promise((resolve, reject) => {
969
- HTTP.patch(`/documents/${state.documentId}/`, updatedDocument)
970
- .then((response) => {
971
- if (response.status === 200) {
972
- if (updatedDocument.data_file_name) {
973
- // if the only change was the file name, don't reload the page
974
- // only update the file name for the selectedDocument
975
- commit("UPDATE_FILE_NAME", response.data.data_file_name);
976
- } else {
977
- commit("SET_SELECTED_DOCUMENT", response.data);
978
-
979
- dispatch("pollDocumentEndpoint");
980
- }
981
-
982
- resolve(true);
983
- }
984
- })
985
- .catch((error) => {
986
- console.log(error);
987
- // check if review error
988
- if (
989
- error.response &&
990
- error.response.request &&
991
- error.response.request.response
992
- ) {
993
- const is_reviewed = JSON.parse(error.request.response).is_reviewed;
994
- if (is_reviewed && is_reviewed.length > 0) {
995
- const errorData = {
996
- data: [...is_reviewed],
997
- };
998
- reject(errorData);
999
- return;
1000
- }
1001
- }
1002
- reject(error.response);
1003
- });
1004
- });
1005
- },
1006
-
1007
- fetchMissingAnnotations: ({ commit, state, getters }) => {
1008
- return new Promise((resolve, reject) => {
1009
- return HTTP.get(
1010
- `/missing-annotations/?document=${state.documentId}&limit=100`
1011
- )
1012
- .then((response) => {
1013
- commit("SET_MISSING_ANNOTATIONS", response.data.results);
1014
- resolve(true);
1015
- })
1016
- .catch((error) => {
1017
- reject(error.response);
1018
- console.log(error);
1019
- });
1020
- });
1021
- },
1022
-
1023
- addMissingAnnotations: ({ commit, getters }, missingAnnotations) => {
1024
- return new Promise((resolve, reject) => {
1025
- HTTP.post(`/missing-annotations/`, missingAnnotations)
1026
- .then(async (response) => {
1027
- if (response.status === 201) {
1028
- commit("SET_ANNOTATIONS_MARKED_AS_MISSING", null);
1029
- commit("ADD_MISSING_ANNOTATIONS", response.data);
1030
- }
1031
-
1032
- resolve(response);
1033
- })
1034
- .catch((error) => {
1035
- reject(error.response);
1036
- console.log(error);
1037
- });
1038
- });
1039
- },
1040
-
1041
- deleteMissingAnnotation: ({ commit, getters }, id) => {
1042
- return new Promise((resolve, reject) => {
1043
- return HTTP.delete(`/missing-annotations/${id}/`)
1044
- .then((response) => {
1045
- if (response.status === 204) {
1046
- commit("DELETE_MISSING_ANNOTATION", id);
1047
- resolve(true);
1048
- }
1049
- })
1050
- .catch((error) => {
1051
- reject(error.response);
1052
- console.log(error);
1053
- });
1054
- });
1055
- },
1056
-
1057
- updateMultipleAnnotations: ({ state, commit }, annotations) => {
1058
- commit("SET_NEW_ACCEPTED_ANNOTATIONS", annotations.ids);
1059
-
1060
- return new Promise((resolve, reject) => {
1061
- return HTTP.patch(
1062
- `documents/${state.documentId}/update-annotations/`,
1063
- annotations
1064
- )
1065
- .then((response) => {
1066
- if (response.status === 200) {
1067
- response.data.map((annotation) => {
1068
- commit("UPDATE_ANNOTATION", annotation);
1069
- });
1070
- commit("SET_NEW_ACCEPTED_ANNOTATIONS", null);
1071
- resolve(true);
1072
- }
1073
- })
1074
- .catch((error) => {
1075
- console.log(error);
1076
- reject(error.response);
1077
- });
1078
- });
1079
- },
1080
-
1081
- fetchDocumentStatus: ({ state, getters }) => {
1082
- return new Promise((resolve, reject) => {
1083
- return HTTP.get(
1084
- `documents/${state.documentId}/?fields=status_data,labeling_available`
1085
- )
1086
- .then((response) => {
1087
- if (
1088
- getters.isDocumentReadyToBeReviewed(response.data) ||
1089
- getters.waitingForSplittingConfirmation(response.data)
1090
- ) {
1091
- // ready or has splitting suggestions
1092
- return resolve(true);
1093
- } else if (getters.documentHadErrorDuringExtraction(response.data)) {
1094
- // error
1095
- return reject();
1096
- } else {
1097
- // not yet ready
1098
- return resolve(false);
1099
- }
1100
- })
1101
- .catch((error) => {
1102
- reject(error.response);
1103
- console.log(error);
1104
- });
1105
- });
1106
- },
1107
-
1108
- // Get document data
1109
- fetchDocumentData: ({ state }) => {
1110
- return new Promise((resolve, reject) => {
1111
- HTTP.get(`documents/${state.documentId}/`)
1112
- .then((response) => {
1113
- return resolve(response.data);
1114
- })
1115
- .catch((error) => {
1116
- reject(error);
1117
- console.log(error);
1118
- });
1119
- });
1120
- },
1121
-
1122
- // Poll Document endpoint to know if the Document is ready to be reviewed
1123
- // or even if there was an error during the extraction
1124
- pollDocumentEndpoint: ({ dispatch }) => {
1125
- return dispatch("fetchDocumentStatus")
1126
- .then((ready) => {
1127
- if (ready) {
1128
- // Stop document recalculating annotations
1129
- dispatch("endRecalculatingAnnotations");
1130
- dispatch("fetchDocument");
1131
- } else {
1132
- sleep(documentPollDuration);
1133
- dispatch("pollDocumentEndpoint");
1134
- }
1135
- })
1136
- .catch((error) => {
1137
- dispatch("setDocumentError", true);
1138
- console.log(error);
1139
- });
1140
- },
1141
-
1142
- createErrorMessage: (
1143
- { commit, dispatch },
1144
- { error, serverErrorMessage, defaultErrorMessage }
1145
- ) => {
1146
- // check type of error
1147
- if (error && error.status >= 500 && error.status < 600) {
1148
- dispatch("setErrorMessage", serverErrorMessage);
1149
- commit("SET_SERVER_ERROR", true);
1150
- } else if (error.data && error.data.length > 0) {
1151
- dispatch("setErrorMessage", error.data[0]);
1152
- } else {
1153
- dispatch("setErrorMessage", defaultErrorMessage);
1154
- }
1155
- },
1156
-
1157
- closeErrorMessage: ({ commit }) => {
1158
- setTimeout(() => {
1159
- commit("SET_ERROR_MESSAGE", null);
1160
- commit("SET_SHOW_ACTION_ERROR", false);
1161
- commit("SET_SERVER_ERROR", false);
1162
- }, 5000);
1163
- },
1164
-
1165
- contactSupport: ({ rootState }, error) => {
1166
- const url = "https://konfuzio.com/support/";
1167
- const params = `project=${rootState.project.projectId}&email=${rootState.project.currentUser.username}&issue=${error}`;
1168
- const fullUrl = `${url}?${params}`;
1169
-
1170
- window.open(fullUrl, "_blank");
1171
- },
1172
-
1173
- changeCurrentDocument: ({ commit, state, dispatch }, newDocumentId) => {
1174
- // reset splitting suggestions
1175
- if (state.splittingSuggestions) {
1176
- commit("SET_SPLITTING_SUGGESTIONS", null);
1177
- }
1178
-
1179
- if (getURLQueryParam("document") || getURLPath("d")) {
1180
- navigateToNewDocumentURL(state.selectedDocument.id, newDocumentId);
1181
- } else {
1182
- commit("SET_DOC_ID", newDocumentId);
1183
- dispatch("fetchDocument");
1184
- }
1185
- },
1186
- };
1187
-
1188
- const mutations = {
1189
- SET_LOADING: (state, loading) => {
1190
- state.loading = loading;
1191
- },
1192
- SET_DOC_ID: (state, id) => {
1193
- if (id !== state.documentId) {
1194
- state.documentId = id;
1195
- }
1196
- },
1197
- ADD_ANNOTATION: (state, annotation) => {
1198
- state.annotations.push(annotation);
1199
- state.annotationSets.map((annotationSet) => {
1200
- if (
1201
- annotation.annotation_set === annotationSet.id &&
1202
- annotation.label_set &&
1203
- annotationSet.label_set.id === annotation.label_set.id
1204
- ) {
1205
- annotationSet.labels.map((label) => {
1206
- if (annotation.label && annotation.label.id === label.id) {
1207
- const exists = label.annotations.find(
1208
- (existingAnnotation) => existingAnnotation.id === annotation.id
1209
- );
1210
-
1211
- if (!exists) {
1212
- label.annotations.push(annotation);
1213
- return;
1214
- }
1215
- }
1216
- });
1217
- }
1218
- });
1219
- },
1220
- UPDATE_ANNOTATION: (state, annotation) => {
1221
- const indexOfAnnotationInAnnotations = state.annotations.findIndex(
1222
- (existingAnnotation) => existingAnnotation.id === annotation.id
1223
- );
1224
- if (indexOfAnnotationInAnnotations > -1) {
1225
- state.annotations.splice(indexOfAnnotationInAnnotations, 1, annotation);
1226
- }
1227
- let updatedAnnotation = false;
1228
- state.annotationSets.forEach((annotationSet) => {
1229
- if (updatedAnnotation) {
1230
- return;
1231
- }
1232
- annotationSet.labels.forEach((label) => {
1233
- const indexOfAnnotationAnnotationSets = label.annotations.findIndex(
1234
- (existingAnnotation) => existingAnnotation.id === annotation.id
1235
- );
1236
- if (indexOfAnnotationAnnotationSets > -1) {
1237
- // checks if an annotation label was changed and add it to the new label
1238
- if (annotation.label && annotation.label.id !== label.id) {
1239
- label.annotations.splice(indexOfAnnotationAnnotationSets, 1);
1240
-
1241
- const labelToAdd = annotationSet.labels.find(
1242
- (labelToAdd) => labelToAdd.id === annotation.label.id
1243
- );
1244
- labelToAdd.annotations.push(annotation);
1245
- } else {
1246
- label.annotations.splice(
1247
- indexOfAnnotationAnnotationSets,
1248
- 1,
1249
- annotation
1250
- );
1251
- }
1252
- updatedAnnotation = true;
1253
- return;
1254
- }
1255
- });
1256
- });
1257
- },
1258
- DELETE_ANNOTATION: (state, annotationId) => {
1259
- const indexOfAnnotationToDelete = state.annotations.findIndex(
1260
- (existingAnnotation) => existingAnnotation.id === annotationId
1261
- );
1262
- if (indexOfAnnotationToDelete > -1) {
1263
- state.annotations.splice(indexOfAnnotationToDelete, 1);
1264
- }
1265
- let deleted = false;
1266
- state.annotationSets.forEach((annotationSet) => {
1267
- if (deleted) {
1268
- return;
1269
- }
1270
- annotationSet.labels.forEach((label) => {
1271
- const indexOfAnnotationInLabelToDelete = label.annotations.findIndex(
1272
- (existingAnnotation) => existingAnnotation.id === annotationId
1273
- );
1274
- if (indexOfAnnotationInLabelToDelete > -1) {
1275
- label.annotations.splice(indexOfAnnotationInLabelToDelete, 1);
1276
- deleted = true;
1277
- return;
1278
- }
1279
- });
1280
- });
1281
- },
1282
- SET_ANNOTATIONS: (state, annotations) => {
1283
- state.annotations = annotations;
1284
- },
1285
- SET_ANNOTATION_SETS: (state, annotationSets) => {
1286
- state.annotationSets = annotationSets;
1287
- },
1288
- DELETE_ANNOTATION_SET: (state, annotationSet) => {
1289
- const indexOfSetToDelete = state.annotationSets.findIndex(
1290
- (existingAnnotationSet) => existingAnnotationSet.id === annotationSet.id
1291
- );
1292
-
1293
- if (indexOfSetToDelete === -1) return;
1294
-
1295
- state.annotationSets.splice(indexOfSetToDelete, 1);
1296
- },
1297
- UPDATE_ANNOTATION_SET: (state, annotationSet) => {
1298
- const indexOfExistingAnnotationSet = state.annotationSets.findIndex(
1299
- (existingAnnotationSet) => existingAnnotationSet.id === annotationSet.id
1300
- );
1301
-
1302
- if (indexOfExistingAnnotationSet === -1) return;
1303
-
1304
- const updatedSet = { ...annotationSet, id: null };
1305
-
1306
- state.annotationSets.splice(indexOfExistingAnnotationSet, 1, updatedSet);
1307
- },
1308
- SET_LABELS: (state, labels) => {
1309
- state.labels = labels;
1310
- },
1311
- SET_ANNOTATION_SELECTED: (state, annotation) => {
1312
- state.sidebarAnnotationSelected = annotation;
1313
- },
1314
- SET_EDIT_ANNOTATION: (state, editAnnotation) => {
1315
- state.editAnnotation = editAnnotation;
1316
- },
1317
- RESET_EDIT_ANNOTATION: (state) => {
1318
- state.editAnnotation = null;
1319
- },
1320
- ADD_PAGE: (state, page) => {
1321
- // if we already have the page in the state, update it in
1322
- // the pages array instead of creating a new one
1323
- const existingPageIndex = state.pages.findIndex(
1324
- (p) => p.number === page.number
1325
- );
1326
- if (existingPageIndex === -1) {
1327
- state.pages.push(page);
1328
- } else {
1329
- state.pages[existingPageIndex] = page;
1330
- }
1331
- },
1332
- SET_PAGES: (state, pages) => {
1333
- state.pages = pages;
1334
- },
1335
- SET_DOCUMENT_ANNOTATION_SELECTED: (state, documentAnnotationSelected) => {
1336
- state.documentAnnotationSelected = documentAnnotationSelected;
1337
- },
1338
- SET_DOCUMENT_ANNOTATION_SCROLL: (state, scrollTo) => {
1339
- if (state.documentAnnotationSelected) {
1340
- state.documentAnnotationSelected.scrollTo = scrollTo;
1341
- }
1342
- },
1343
- SET_SELECTED_DOCUMENT: (state, document) => {
1344
- state.selectedDocument = document;
1345
-
1346
- // this is to handle cache when a document is edited or changed
1347
- if (state.selectedDocument) {
1348
- state.selectedDocument.downloaded_at = Date.now();
1349
- }
1350
- },
1351
- SET_RECALCULATING_ANNOTATIONS: (state, recalculatingAnnotations) => {
1352
- state.recalculatingAnnotations = recalculatingAnnotations;
1353
- },
1354
- SET_MISSING_ANNOTATIONS: (state, missingAnnotations) => {
1355
- state.missingAnnotations = missingAnnotations;
1356
- },
1357
- ADD_MISSING_ANNOTATIONS: (state, annotations) => {
1358
- if (annotations && annotations.length > 0) {
1359
- annotations.map((annotation) => {
1360
- // check if already in missingAnnotations
1361
- const found = state.missingAnnotations.find(
1362
- (missingAnnotation) => missingAnnotation.id === annotation.id
1363
- );
1364
-
1365
- if (found) {
1366
- const indexOfAnnotation = state.missingAnnotations.findIndex(
1367
- (missingAnnotation) => missingAnnotation.id === annotation.id
1368
- );
1369
-
1370
- if (indexOfAnnotation > -1) {
1371
- state.missingAnnotations.splice(indexOfAnnotation, 1, annotation);
1372
- }
1373
- } else {
1374
- state.missingAnnotations.push(annotation);
1375
- }
1376
- });
1377
- } else {
1378
- state.missingAnnotations.push(annotations);
1379
- }
1380
- },
1381
- DELETE_MISSING_ANNOTATION: (state, id) => {
1382
- const indexOfAnnotationToDelete = state.missingAnnotations.findIndex(
1383
- (annotation) => annotation.id === id
1384
- );
1385
-
1386
- if (indexOfAnnotationToDelete > -1) {
1387
- state.missingAnnotations.splice(indexOfAnnotationToDelete, 1);
1388
- }
1389
- },
1390
- SET_SHOW_ACTION_ERROR: (state, value) => {
1391
- state.showActionError = value;
1392
- },
1393
- SET_ERROR_MESSAGE: (state, message) => {
1394
- state.errorMessage = message;
1395
- },
1396
- SET_DOCUMENT_ERROR: (state, value) => {
1397
- state.showDocumentError = value;
1398
- },
1399
- SET_ANNOTATIONS_MARKED_AS_MISSING: (state, annotations) => {
1400
- state.annotationsMarkedAsMissing = annotations;
1401
- },
1402
- SET_ERROR_MESSAGE_WIDTH: (state, width) => {
1403
- state.errorMessageWidth = width;
1404
- },
1405
- SET_PUBLIC_VIEW: (state, value) => {
1406
- state.publicView = value;
1407
- },
1408
- SET_DOCUMENT_IS_READY: (state, value) => {
1409
- state.documentIsReady = value;
1410
- },
1411
- SET_DOCUMENT_HAS_ERROR: (state, value) => {
1412
- state.documentHasError = value;
1413
- },
1414
- SET_HOVERED_ANNOTATION_SET: (state, hoveredAnnotationSet) => {
1415
- state.hoveredAnnotationSet = hoveredAnnotationSet;
1416
- },
1417
- SET_NEW_ACCEPTED_ANNOTATIONS: (state, annotations) => {
1418
- state.newAcceptedAnnotations = annotations;
1419
- },
1420
- SET_SERVER_ERROR: (state, value) => {
1421
- state.serverError = value;
1422
- },
1423
-
1424
- UPDATE_FILE_NAME: (state, value) => {
1425
- state.selectedDocument.data_file_name = value;
1426
- },
1427
- SET_SPLITTING_SUGGESTIONS: (state, array) => {
1428
- state.splittingSuggestions = array;
1429
- },
1430
- };
1431
-
1432
- export default {
1433
- namespaced: true,
1434
- state,
1435
- getters,
1436
- actions,
1437
- mutations,
1438
- };
1
+ import myImports from "../api";
2
+ import { MULTI_ANN_TABLE_FEATURE } from "../constants";
3
+ import {
4
+ sleep,
5
+ getURLQueryParam,
6
+ navigateToNewDocumentURL,
7
+ getURLPath,
8
+ } from "../utils/utils";
9
+
10
+ const HTTP = myImports.HTTP;
11
+ const documentPollDuration = 1000;
12
+ export const table_reference_api = "api.v3.dvui.table";
13
+
14
+ const state = {
15
+ loading: true,
16
+ pages: [],
17
+ annotationSets: null,
18
+ annotations: null,
19
+ labels: [],
20
+ documentId: process.env.VUE_APP_DOCUMENT_ID,
21
+ sidebarAnnotationSelected: null,
22
+ documentAnnotationSelected: null,
23
+ selectedDocument: null,
24
+ recalculatingAnnotations: false,
25
+ editAnnotation: null,
26
+ missingAnnotations: [],
27
+ publicView: true,
28
+ showActionError: false,
29
+ errorMessage: null,
30
+ showDocumentError: false,
31
+ annotationsMarkedAsMissing: null,
32
+ errorMessageWidth: null,
33
+ hoveredAnnotationSet: null,
34
+ newAcceptedAnnotations: null,
35
+ serverError: false,
36
+ splittingSuggestions: null,
37
+ enableGroupingFeature: true,
38
+ };
39
+
40
+ const getters = {
41
+ /**
42
+ * Get entities inside a box
43
+ */
44
+ entitiesOnSelection: (state) => (box, page) => {
45
+ return page.entities.filter(
46
+ (entity) =>
47
+ box.x0 <= entity.x0 &&
48
+ box.x1 >= entity.x1 &&
49
+ box.y0 <= entity.y0 &&
50
+ box.y1 >= entity.y1
51
+ );
52
+ },
53
+
54
+ /**
55
+ * Number of pages. If the pages array doesn't exist yet, return 0.
56
+ */
57
+ pageCount: (state) => {
58
+ if (state.selectedDocument.pages) {
59
+ return state.selectedDocument.pages.length;
60
+ }
61
+ return 0;
62
+ },
63
+
64
+ /**
65
+ * Returns the current page
66
+ */
67
+ pageSelected: (state, _, rootState) => {
68
+ if (state.pages) {
69
+ return state.pages[rootState.display.currentPage - 1];
70
+ }
71
+ return null;
72
+ },
73
+
74
+ /**
75
+ * Returns a page in the given index
76
+ */
77
+ pageAtIndex: (state) => (index) => {
78
+ if (state.selectedDocument && state.selectedDocument.pages) {
79
+ return state.selectedDocument.pages[index];
80
+ }
81
+ return null;
82
+ },
83
+
84
+ /**
85
+ * Checks if is to scroll to an annotation in the document
86
+ */
87
+ scrollDocumentToAnnotation: (state) => {
88
+ return (
89
+ state.documentAnnotationSelected &&
90
+ state.documentAnnotationSelected.scrollTo
91
+ );
92
+ },
93
+
94
+ /**
95
+ * Checks if the document is categorized and ready to start the review
96
+ */
97
+ categorizationIsConfirmed: (state) => {
98
+ if (state.selectedDocument) {
99
+ if (
100
+ state.selectedDocument.category_is_revised ||
101
+ state.selectedDocument.is_reviewed
102
+ ) {
103
+ return true;
104
+ } else if (!state.selectedDocument.category) {
105
+ return false;
106
+ } else {
107
+ // check if there's any annotation already approved
108
+ const found = state.annotations.find((annotation) => {
109
+ return annotation.revised;
110
+ });
111
+ return found != undefined;
112
+ }
113
+ }
114
+ return false;
115
+ },
116
+
117
+ /**
118
+ * Gets labels for an annotation creation from a label/annotation set
119
+ */
120
+ labelsFilteredForAnnotationCreation: (_, getters) => (set) => {
121
+ let availableLabels = [];
122
+ if (set.id && set.labels) {
123
+ // check if label can be multiple, if there's already an annotation created & if it's a negative annotation
124
+ set.labels.map((label) => {
125
+ // check if we already added the same label to the array
126
+ const found = availableLabels.find((l) => l.id === label.id);
127
+
128
+ if (found) return;
129
+
130
+ if (label.annotations.length === 0) {
131
+ availableLabels.push(label);
132
+ } else {
133
+ if (label.has_multiple_top_candidates) {
134
+ availableLabels.push(label);
135
+ } else {
136
+ // if the label has negative annotations, we show the label
137
+ label.annotations.map((annotation) => {
138
+ if (getters.isNegative(annotation)) {
139
+ availableLabels.push(label);
140
+ }
141
+ });
142
+ }
143
+ }
144
+ });
145
+ } else if (set.labels) {
146
+ // if not existing ann set, then return all labels
147
+ availableLabels = set.labels;
148
+ }
149
+ return availableLabels;
150
+ },
151
+
152
+ /* Checks if annotation is in deleted state */
153
+ isAnnotationDeleted: (state) => (annotation) => {
154
+ if (annotation) {
155
+ return annotation.revised && !annotation.is_correct;
156
+ }
157
+ return false;
158
+ },
159
+
160
+ /* Checks if the label has annotations to show */
161
+ labelHasAnnotations: (_, getters) => (label) => {
162
+ const annotations = label.annotations.filter((annotation) => {
163
+ return !getters.isAnnotationDeleted(annotation);
164
+ });
165
+ return annotations.length > 0;
166
+ },
167
+
168
+ /* Checks if the document has an annotation set */
169
+ annotationSetExists: (state) => (annotationSetId) => {
170
+ return state.annotationSets.find((annSet) => annSet.id === annotationSetId);
171
+ },
172
+
173
+ /* Get annotation sets created in table */
174
+ annotationSetsInTable: (state) => () => {
175
+ const annotationSetsList = {};
176
+ if (MULTI_ANN_TABLE_FEATURE) {
177
+ state.annotationSets.forEach((annotationSet) => {
178
+ let addAnnotationSet = false;
179
+ if (annotationSet.labels) {
180
+ annotationSet.labels.forEach((label) => {
181
+ if (
182
+ label.annotations &&
183
+ label.annotations.find(
184
+ (annotation) =>
185
+ annotation.origin && annotation.origin === table_reference_api
186
+ )
187
+ ) {
188
+ addAnnotationSet = true;
189
+ return;
190
+ }
191
+ });
192
+ }
193
+ if (addAnnotationSet) {
194
+ // group by label set
195
+ if (annotationSetsList[`${annotationSet.label_set.id}`]) {
196
+ annotationSetsList[`${annotationSet.label_set.id}`].push(
197
+ annotationSet
198
+ );
199
+ } else {
200
+ annotationSetsList[`${annotationSet.label_set.id}`] = [
201
+ annotationSet,
202
+ ];
203
+ }
204
+ }
205
+ });
206
+ }
207
+ return annotationSetsList;
208
+ },
209
+
210
+ /* Get annotation sets without tables */
211
+ annotationSetsToShowInList: (state) => () => {
212
+ const annotationSetsList = [];
213
+ state.annotationSets.forEach((annotationSet) => {
214
+ let addAnnotationSet = true;
215
+ if (annotationSet.labels) {
216
+ annotationSet.labels.forEach((label) => {
217
+ if (
218
+ MULTI_ANN_TABLE_FEATURE &&
219
+ label.annotations &&
220
+ label.annotations.find(
221
+ (annotation) =>
222
+ annotation.origin && annotation.origin === table_reference_api
223
+ )
224
+ ) {
225
+ addAnnotationSet = false;
226
+ return;
227
+ }
228
+ });
229
+ }
230
+ if (addAnnotationSet) {
231
+ annotationSetsList.push(annotationSet);
232
+ }
233
+ });
234
+ return annotationSetsList;
235
+ },
236
+
237
+ /* Get annotations inside a list of annotation sets */
238
+ annotationsInAnnotationsSets: (state) => (annotationsSets) => {
239
+ const annotations = [];
240
+ annotationsSets.forEach((annotationSet) => {
241
+ annotationSet.labels.forEach((label) => {
242
+ label.annotations.forEach((annotation) => {
243
+ annotations.push(annotation);
244
+ });
245
+ });
246
+ });
247
+ return annotations;
248
+ },
249
+
250
+ /* Process annotations and extract labels and sets */
251
+ processAnnotationSets: (state, getters) => (annotationSets) => {
252
+ // group annotations for sidebar
253
+ const annotations = [];
254
+ const labels = [];
255
+ const processedAnnotationSets = annotationSets.map((annotationSet) => {
256
+ const annotationSetLabels = annotationSet.labels.map((label) => {
257
+ // add annotations to the document array
258
+ annotations.push(...label.annotations);
259
+ labels.push(label);
260
+ // add labels to the labels array
261
+ return label;
262
+ });
263
+ annotationSet.labels = annotationSetLabels;
264
+ return annotationSet;
265
+ });
266
+
267
+ return {
268
+ annotationSets: processedAnnotationSets,
269
+ labels,
270
+ annotations,
271
+ };
272
+ },
273
+
274
+ /* Checks if there are annotations correct in the document */
275
+ documentHasCorrectAnnotations: (state) => {
276
+ if (
277
+ !state.annotations ||
278
+ (state.annotations &&
279
+ state.annotations.filter((ann) => ann.is_correct).length > 0)
280
+ ) {
281
+ return true;
282
+ }
283
+ return false;
284
+ },
285
+
286
+ /* Returns the number of accepted annotations in a label */
287
+ numberOfAcceptedAnnotationsInLabel: (_) => (label) => {
288
+ const annotations = label.annotations.filter((annotation) => {
289
+ return annotation.revised && annotation.is_correct;
290
+ });
291
+ return annotations.length;
292
+ },
293
+
294
+ /**
295
+ * Checks if theres a group of annotation sets and add an index number to them
296
+ */
297
+ numberOfAnnotationSetGroup: (state) => (annotationSet) => {
298
+ let found = false;
299
+ let value = 0;
300
+ let index = 0;
301
+ if (state.annotationSets) {
302
+ state.annotationSets.map((annotationSetTemp) => {
303
+ if (
304
+ annotationSetTemp.id !== annotationSet.id &&
305
+ annotationSetTemp.label_set.id === annotationSet.label_set.id &&
306
+ annotationSetTemp.label_set.name === annotationSet.label_set.name
307
+ ) {
308
+ found = true;
309
+ index++;
310
+ } else if (
311
+ annotationSetTemp.label_set.id === annotationSet.label_set.id
312
+ ) {
313
+ value = index;
314
+ }
315
+ });
316
+ return found ? `${value + 1}` : "";
317
+ }
318
+ return "";
319
+ },
320
+
321
+ /**
322
+ * Get label with annotations filtered if the label supports multiple or not
323
+ */
324
+ annotationsInLabelFiltered: (state) => (label) => {
325
+ let labelToReturn;
326
+ if (
327
+ label.has_multiple_top_candidates === false &&
328
+ label.annotations &&
329
+ label.annotations.length > 1
330
+ ) {
331
+ let highestConfidenceAnnotation = label.annotations[0];
332
+ for (let i = 1; i < label.annotations.length; i++) {
333
+ // check which one has more confidence or if it's the same, then check if one is revised or not
334
+ if (
335
+ highestConfidenceAnnotation.confidence <
336
+ label.annotations[i].confidence ||
337
+ (highestConfidenceAnnotation.confidence ===
338
+ label.annotations[i].confidence &&
339
+ label.annotations[i].revised)
340
+ ) {
341
+ highestConfidenceAnnotation = label.annotations[i];
342
+ }
343
+ }
344
+ labelToReturn = {
345
+ ...label,
346
+ annotations: [highestConfidenceAnnotation],
347
+ };
348
+ } else {
349
+ labelToReturn = {
350
+ ...label,
351
+ };
352
+ }
353
+ return labelToReturn;
354
+ },
355
+
356
+ /**
357
+ * Checks if annotation is being edited
358
+ */
359
+ isAnnotationInEditMode:
360
+ (state) =>
361
+ (annotationId, index = null) => {
362
+ if (state.editAnnotation && annotationId) {
363
+ if (index != null) {
364
+ return (
365
+ state.editAnnotation.id === annotationId &&
366
+ state.editAnnotation.index === index
367
+ );
368
+ }
369
+ return state.editAnnotation.id === annotationId;
370
+ }
371
+ },
372
+
373
+ /**
374
+ * Get number of empty labels per annotation set
375
+ */
376
+ emptyLabels: (state, getters) => (annotationSet) => {
377
+ const pendingEmpty = [];
378
+
379
+ annotationSet.labels.map((label) => {
380
+ const foundMissing = state.missingAnnotations.find(
381
+ (l) =>
382
+ l.label === label.id &&
383
+ annotationSet.id === l.annotation_set &&
384
+ annotationSet.label_set.id === l.label_set
385
+ );
386
+
387
+ const foundNegative = label.annotations.find((annotation) =>
388
+ getters.isNegative(annotation)
389
+ );
390
+
391
+ if (!foundMissing && (label.annotations.length === 0 || foundNegative)) {
392
+ pendingEmpty.push(label);
393
+ }
394
+ });
395
+
396
+ return pendingEmpty;
397
+ },
398
+
399
+ annotationIsNotFound: (state) => (annotationSet, label) => {
400
+ // Check if the combined label and label set have been marked as missing
401
+ // or if the document is in public mode
402
+ if (state.missingAnnotations.length === 0) {
403
+ return false;
404
+ } else {
405
+ const found = state.missingAnnotations.filter(
406
+ (el) =>
407
+ el.label === label.id &&
408
+ el.annotation_set === annotationSet.id &&
409
+ el.label_set === annotationSet.label_set.id
410
+ );
411
+ return found.length !== 0;
412
+ }
413
+ },
414
+
415
+ // Check if document is ready to be finished
416
+ isDocumentReadyToFinishReview: (state) => {
417
+ // check if all annotations are correct
418
+ let notCorrect;
419
+
420
+ const emptyAnnotations = [];
421
+
422
+ if (state.labels.length === 0) return false;
423
+
424
+ if (state.annotationSets && state.annotationSets.length > 0) {
425
+ state.annotationSets.forEach((annSet) => {
426
+ annSet.labels.map((label) => {
427
+ // return only labels with empty annotations
428
+ if (label.annotations.length === 0) {
429
+ emptyAnnotations.push({
430
+ label: label.id,
431
+ label_set: annSet.label_set.id,
432
+ });
433
+ }
434
+ });
435
+ });
436
+ }
437
+
438
+ if (state.annotations) {
439
+ notCorrect = state.annotations.filter((a) => !a.is_correct);
440
+
441
+ if (notCorrect) {
442
+ notCorrect.map((annotation) => {
443
+ emptyAnnotations.push(annotation);
444
+ });
445
+ }
446
+ }
447
+
448
+ // if all annotations are correct
449
+ // and if there are no empty annotations or
450
+ // all empty annotations or negative annotations were marked as missing,
451
+ // we can finish the review
452
+ if (
453
+ !emptyAnnotations ||
454
+ !state.missingAnnotations ||
455
+ state.missingAnnotations.length === emptyAnnotations.length
456
+ ) {
457
+ return true;
458
+ }
459
+ return false;
460
+ },
461
+
462
+ isDocumentReviewed: (state) => {
463
+ return state.selectedDocument.is_reviewed;
464
+ },
465
+
466
+ /**
467
+ * Get number of annotations pending review per annotation set
468
+ */
469
+ notCorrectAnnotations: () => (annotationSet) => {
470
+ const labels = annotationSet.labels.filter(
471
+ (label) => label.annotations.length > 0
472
+ );
473
+
474
+ const annotationsWithPendingReview = [];
475
+
476
+ labels.map((label) => {
477
+ const foundPendingAnnotations = label.annotations.filter(
478
+ (ann) => !ann.is_correct && !ann.revised
479
+ );
480
+
481
+ if (foundPendingAnnotations && foundPendingAnnotations.length > 0) {
482
+ foundPendingAnnotations.map((annotation) => {
483
+ annotationsWithPendingReview.push(annotation);
484
+ });
485
+ }
486
+
487
+ // Check if we have grouped annotations by same label
488
+ if (state.enableGroupingFeature && label.annotations.length < 2) {
489
+ return annotationsWithPendingReview.length - label.annotations.length;
490
+ }
491
+ });
492
+
493
+ return annotationsWithPendingReview;
494
+ },
495
+
496
+ /**
497
+ * Check if the document was extracted correctly and is ready to be reviewed
498
+ */
499
+ isDocumentReadyToBeReviewed: () => (document) => {
500
+ return document.status_data === 2 && document.labeling_available === 1;
501
+ },
502
+
503
+ /**
504
+ * Check if the document had an error during extraction
505
+ */
506
+ documentHadErrorDuringExtraction: () => (document) => {
507
+ return document.status_data === 111;
508
+ },
509
+
510
+ /**
511
+ * check if the document has a dataset status of 'Training', 'Test' or 'Preparation'
512
+ * or if it is Ready Only / Reviewed
513
+ * and if so disable the option to edit the document
514
+ */
515
+ documentCannotBeEdited:
516
+ (state) =>
517
+ (document = state.selectedDocument) => {
518
+ return (
519
+ document.dataset_status === 1 ||
520
+ document.dataset_status === 2 ||
521
+ document.dataset_status === 3 ||
522
+ document.is_reviewed
523
+ );
524
+ },
525
+
526
+ /**
527
+ * If automatic splitting is enabled for the project
528
+ */
529
+ waitingForSplittingConfirmation: () => (document) => {
530
+ return document && document.status_data === 41;
531
+ },
532
+
533
+ /**
534
+ * Show the Smart Split switch or not
535
+ */
536
+ documentHasProposedSplit: () => (document) => {
537
+ return document.proposed_split && document.proposed_split.length > 0;
538
+ },
539
+
540
+ /**
541
+ * Check the level of confidence of an annotation
542
+ */
543
+ confidence: () => (annotation) => {
544
+ if (annotation) {
545
+ return annotation.confidence;
546
+ } else {
547
+ return null;
548
+ }
549
+ },
550
+
551
+ /**
552
+ * Check status of annotation
553
+ */
554
+ notExtracted: () => (annotation) => {
555
+ if (annotation) {
556
+ return !annotation.span;
557
+ } else {
558
+ return true;
559
+ }
560
+ },
561
+ created: () => (annotation) => {
562
+ if (annotation) {
563
+ return (
564
+ annotation.created_by && !annotation.revised && annotation.is_correct
565
+ );
566
+ } else {
567
+ return null;
568
+ }
569
+ },
570
+ edited: () => (annotation) => {
571
+ if (annotation) {
572
+ if (annotation.offset_string !== annotation.offset_string_original) {
573
+ return true;
574
+ } else if (annotation.created_by) {
575
+ return true;
576
+ } else {
577
+ return false;
578
+ }
579
+ } else {
580
+ return null;
581
+ }
582
+ },
583
+ accepted: () => (annotation) => {
584
+ if (annotation) {
585
+ return annotation.revised && annotation.is_correct;
586
+ } else {
587
+ return null;
588
+ }
589
+ },
590
+ isNegative: () => (annotation) => {
591
+ if (annotation) {
592
+ return !annotation.is_correct && annotation.revised;
593
+ } else {
594
+ return null;
595
+ }
596
+ },
597
+
598
+ /**
599
+ * Check for user who created or revised the annotation
600
+ */
601
+ getUser: () => (annotation) => {
602
+ if (annotation) {
603
+ if (annotation.created_by && !annotation.revised) {
604
+ // If the annotation was created but not yet revised
605
+ // we show who created it
606
+ return annotation.created_by;
607
+ } else if (annotation.revised && annotation.revised_by) {
608
+ return annotation.revised_by;
609
+ } else {
610
+ // If both revised_by and created_by are null, we don't show any user
611
+ return null;
612
+ }
613
+ } else {
614
+ return null;
615
+ }
616
+ },
617
+
618
+ /**
619
+ * Check if there is just one annotation set from a label set
620
+ */
621
+ isOnlyMultipleAnnotationSet: (state) => (annotationSet) => {
622
+ const sameSets = state.annotationSets.filter(set => set.label_set.id === annotationSet.label_set.id);
623
+
624
+ return sameSets.length === 1 ? true : false;
625
+ },
626
+
627
+ /**
628
+ * Check if the annotation set can appear multiple times
629
+ */
630
+ annotationSetCanBeMultiple: (_) => (annotationSet) => {
631
+ return annotationSet.label_set.has_multiple_annotation_sets;
632
+ },
633
+
634
+ /**
635
+ * Check if the annotation set has only empty labels
636
+ */
637
+ annotationSetHasNoFilledLabels: (_) => (annotationSet) => {
638
+ const annotations = annotationSet.labels.flatMap(label => {
639
+ return label.annotations;
640
+ });
641
+
642
+ console.log(annotations);
643
+
644
+ return annotations.length === 0 ? true : false;
645
+ },
646
+ };
647
+
648
+ const actions = {
649
+ startLoading: ({ commit }) => {
650
+ commit("SET_LOADING", true);
651
+ },
652
+ endLoading: ({ commit }) => {
653
+ commit("SET_LOADING", false);
654
+ },
655
+ setDocId: ({ commit }, id) => {
656
+ commit("SET_PAGES", []);
657
+ commit("SET_DOC_ID", id);
658
+ },
659
+ setSidebarAnnotationSelected: ({ commit }, annotation) => {
660
+ commit("SET_ANNOTATION_SELECTED", annotation);
661
+ },
662
+ setAnnotationSets: ({ commit }, annotationSets) => {
663
+ commit("SET_ANNOTATION_SETS", annotationSets);
664
+ },
665
+ setEditAnnotation: (
666
+ { commit },
667
+ { id, index, label, labelSet, annotationSet }
668
+ ) => {
669
+ const value = {
670
+ id,
671
+ index,
672
+ label,
673
+ labelSet,
674
+ annotationSet,
675
+ };
676
+ commit("SET_EDIT_ANNOTATION", value);
677
+ },
678
+ resetEditAnnotation: ({ commit }) => {
679
+ commit("RESET_EDIT_ANNOTATION");
680
+ },
681
+ setAnnotations: ({ commit }, annotations) => {
682
+ commit("SET_ANNOTATIONS", annotations);
683
+ },
684
+ setLabels: ({ commit }, labels) => {
685
+ commit("SET_LABELS", labels);
686
+ },
687
+ setPages: ({ commit }, pages) => {
688
+ commit("SET_PAGES", pages);
689
+ },
690
+ setSelectedDocument: ({ commit }, document) => {
691
+ commit("SET_SELECTED_DOCUMENT", document);
692
+ },
693
+ setPublicView: ({ commit }, publicView) => {
694
+ commit("SET_PUBLIC_VIEW", publicView);
695
+ },
696
+ startRecalculatingAnnotations: ({ commit }) => {
697
+ commit("SET_RECALCULATING_ANNOTATIONS", true);
698
+ },
699
+ endRecalculatingAnnotations: ({ commit }) => {
700
+ commit("SET_RECALCULATING_ANNOTATIONS", false);
701
+ },
702
+ setMissingAnnotations: ({ commit }, missingAnnotations) => {
703
+ commit("SET_MISSING_ANNOTATIONS", missingAnnotations);
704
+ },
705
+ setErrorMessage: ({ commit, dispatch }, message) => {
706
+ if (message) {
707
+ commit("SET_SHOW_ACTION_ERROR", true);
708
+ } else {
709
+ commit("SET_SHOW_ACTION_ERROR", false);
710
+ }
711
+
712
+ commit("SET_ERROR_MESSAGE", message);
713
+
714
+ dispatch("closeErrorMessage");
715
+ },
716
+ setDocumentError: ({ commit }, value) => {
717
+ commit("SET_DOCUMENT_ERROR", value);
718
+ },
719
+ setAnnotationsMarkedAsMissing: ({ commit }, annotations) => {
720
+ commit("SET_ANNOTATIONS_MARKED_AS_MISSING", annotations);
721
+ },
722
+ setErrorMessageWidth: ({ commit }, width) => {
723
+ commit("SET_ERROR_MESSAGE_WIDTH", width);
724
+ },
725
+ setHoveredAnnotationSet: ({ commit }, annotationSet) => {
726
+ commit("SET_HOVERED_ANNOTATION_SET", annotationSet);
727
+ },
728
+ setNewAcceptedAnnotations: ({ commit }, annotations) => {
729
+ commit("SET_NEW_ACCEPTED_ANNOTATIONS", annotations);
730
+ },
731
+ setSplittingSuggestions: ({ commit }, value) => {
732
+ commit("SET_SPLITTING_SUGGESTIONS", value);
733
+ },
734
+
735
+ /**
736
+ * Actions that use HTTP requests always return the axios promise,
737
+ * so they can be `await`ed (useful to set the `loading` status).
738
+ */
739
+ fetchDocument: async (
740
+ { commit, state, dispatch, rootState, getters },
741
+ pollDocumentList = false
742
+ ) => {
743
+ let projectId = null;
744
+ let categoryId = null;
745
+ let isRecalculatingAnnotations = false;
746
+
747
+ const initialPage = 1;
748
+
749
+ dispatch("startLoading");
750
+ dispatch("display/updateCurrentPage", initialPage, {
751
+ root: true,
752
+ });
753
+
754
+ await HTTP.get(`documents/${state.documentId}/`)
755
+ .then(async (response) => {
756
+ if (response.data) {
757
+ const { labels, annotations, annotationSets } =
758
+ getters.processAnnotationSets(response.data.annotation_sets);
759
+
760
+ // load first page
761
+ if (response.data.pages.length > 0) {
762
+ dispatch("fetchDocumentPage", initialPage);
763
+ }
764
+
765
+ // set information on the store
766
+ commit("SET_ANNOTATION_SETS", annotationSets);
767
+ commit("SET_ANNOTATIONS", annotations);
768
+ commit("SET_LABELS", labels);
769
+ commit("SET_SELECTED_DOCUMENT", response.data);
770
+
771
+ if (response.data.project) {
772
+ projectId = response.data.project;
773
+
774
+ dispatch("project/setProjectId", projectId, {
775
+ root: true,
776
+ });
777
+ dispatch("project/fetchProjectDetails", projectId, {
778
+ root: true,
779
+ });
780
+ }
781
+
782
+ if (getters.documentHasProposedSplit(response.data)) {
783
+ commit("SET_SPLITTING_SUGGESTIONS", response.data.proposed_split);
784
+ }
785
+
786
+ categoryId = response.data.category;
787
+ // TODO: add this validation to a method
788
+ isRecalculatingAnnotations = response.data.labeling_available !== 1;
789
+ }
790
+ })
791
+ .catch((error) => {
792
+ console.log(error, "Could not fetch document details from the backend");
793
+ return;
794
+ });
795
+
796
+ if (!state.publicView) {
797
+ await dispatch("fetchMissingAnnotations");
798
+
799
+ await dispatch("project/fetchCurrentUser", null, {
800
+ root: true,
801
+ });
802
+
803
+ // Check if we first open the document dashboard or the edit mode
804
+ if (
805
+ !state.selectedDocument.category ||
806
+ (!state.selectedDocument.category_is_revised &&
807
+ !getters.documentHasCorrectAnnotations &&
808
+ state.missingAnnotations.length === 0)
809
+ ) {
810
+ dispatch("edit/enableEditMode", null, {
811
+ root: true,
812
+ });
813
+ dispatch("edit/setRenameAndCategorize", true, { root: true });
814
+ }
815
+
816
+ if (projectId) {
817
+ await dispatch("category/fetchCategories", projectId, {
818
+ root: true,
819
+ });
820
+
821
+ // get list of documents not reviewed
822
+ await dispatch("project/fetchDocumentList", "is_reviewed=false", {
823
+ root: true,
824
+ });
825
+ }
826
+ if (categoryId && rootState.category.createAvailableListOfDocuments) {
827
+ await dispatch(
828
+ "category/createAvailableDocumentsList",
829
+ {
830
+ categoryId,
831
+ user: rootState.project.currentUser.username,
832
+ poll: pollDocumentList,
833
+ },
834
+ {
835
+ root: true,
836
+ }
837
+ );
838
+ }
839
+ }
840
+ if (isRecalculatingAnnotations) {
841
+ commit("SET_RECALCULATING_ANNOTATIONS", true);
842
+ dispatch("pollDocumentEndpoint");
843
+ }
844
+ dispatch("endLoading");
845
+ },
846
+
847
+ // Get document page data
848
+ fetchDocumentPage: ({ commit, state }, page) => {
849
+ return HTTP.get(`documents/${state.documentId}/pages/${page}/`)
850
+ .then((response) => {
851
+ commit("ADD_PAGE", response.data);
852
+ })
853
+ .catch((error) => {
854
+ console.log(error);
855
+ });
856
+ },
857
+
858
+ setDocumentAnnotationSelected: (
859
+ { commit },
860
+ { annotation, label, span, scrollTo = false }
861
+ ) => {
862
+ const value = {
863
+ scrollTo,
864
+ id: annotation.id,
865
+ span,
866
+ page: span.page_index + 1,
867
+ labelName: label ? label.name : "",
868
+ };
869
+ commit("SET_DOCUMENT_ANNOTATION_SELECTED", value);
870
+ },
871
+
872
+ scrollToDocumentAnnotationSelected: ({ commit }) => {
873
+ commit("SET_DOCUMENT_ANNOTATION_SCROLL", true);
874
+ },
875
+
876
+ disableDocumentAnnotationSelected: ({ commit }) => {
877
+ commit("SET_DOCUMENT_ANNOTATION_SELECTED", null);
878
+ },
879
+
880
+ createAnnotation: (
881
+ { commit, getters, dispatch },
882
+ { annotation, negativeAnnotationId }
883
+ ) => {
884
+ return new Promise((resolve, reject) => {
885
+ HTTP.post(`/annotations/`, annotation)
886
+ .then(async (response) => {
887
+ if (response.status === 201) {
888
+ await dispatch("fetchMissingAnnotations");
889
+
890
+ if (!getters.annotationSetExists(response.data.annotation_set)) {
891
+ const documentData = await dispatch("fetchDocumentData");
892
+ if (documentData && documentData.annotation_sets) {
893
+ const { labels, annotations, annotationSets } =
894
+ getters.processAnnotationSets(documentData.annotation_sets);
895
+ commit("SET_ANNOTATION_SETS", annotationSets);
896
+ commit("SET_ANNOTATIONS", annotations);
897
+ commit("SET_LABELS", labels);
898
+ }
899
+ } else {
900
+ commit("ADD_ANNOTATION", response.data);
901
+ if (negativeAnnotationId) {
902
+ commit("DELETE_ANNOTATION", negativeAnnotationId);
903
+ }
904
+ }
905
+
906
+ resolve(response);
907
+ }
908
+ })
909
+ .catch((error) => {
910
+ reject(error.response);
911
+ console.log(error);
912
+ });
913
+ });
914
+ },
915
+
916
+ updateAnnotation: (
917
+ { commit, getters, dispatch },
918
+ { updatedValues, annotationId }
919
+ ) => {
920
+ commit("SET_NEW_ACCEPTED_ANNOTATIONS", [annotationId]);
921
+
922
+ return new Promise((resolve, reject) => {
923
+ HTTP.patch(`/annotations/${annotationId}/`, updatedValues)
924
+ .then(async (response) => {
925
+ if (response.status === 200) {
926
+ commit("UPDATE_ANNOTATION", response.data);
927
+ commit("SET_NEW_ACCEPTED_ANNOTATIONS", null);
928
+
929
+ resolve(true);
930
+ }
931
+ })
932
+ .catch((error) => {
933
+ reject(error.response);
934
+ console.log(error);
935
+ });
936
+ });
937
+ },
938
+
939
+ deleteAnnotation: ({ commit, getters }, { annotationId, annotationSet }) => {
940
+ return new Promise((resolve, reject) => {
941
+ HTTP.delete(`/annotations/${annotationId}/`)
942
+ .then(async (response) => {
943
+ if (response.status === 204) {
944
+ commit("DELETE_ANNOTATION", annotationId);
945
+
946
+ // Check if the deleted annotation was the last one in a multiple annotation set
947
+ // and if the annotation set has no annotations
948
+ if (annotationSet && getters.annotationSetCanBeMultiple(annotationSet) && getters.annotationSetHasNoFilledLabels(annotationSet)) {
949
+ // Check if there is still 1 or more multiple annotation sets for the same label set
950
+ if (getters.isOnlyMultipleAnnotationSet(annotationSet)) {
951
+ commit("UPDATE_ANNOTATION_SET", annotationSet);
952
+ } else {
953
+ commit("DELETE_ANNOTATION_SET", annotationSet);
954
+ }
955
+ }
956
+
957
+ resolve(true);
958
+ }
959
+ })
960
+ .catch((error) => {
961
+ reject(error.response);
962
+ console.log(error);
963
+ });
964
+ });
965
+ },
966
+
967
+ updateDocument: ({ commit, state, getters, dispatch }, updatedDocument) => {
968
+ return new Promise((resolve, reject) => {
969
+ HTTP.patch(`/documents/${state.documentId}/`, updatedDocument)
970
+ .then((response) => {
971
+ if (response.status === 200) {
972
+ if (updatedDocument.data_file_name) {
973
+ // if the only change was the file name, don't reload the page
974
+ // only update the file name for the selectedDocument
975
+ commit("UPDATE_FILE_NAME", response.data.data_file_name);
976
+ } else {
977
+ commit("SET_SELECTED_DOCUMENT", response.data);
978
+
979
+ dispatch("pollDocumentEndpoint");
980
+ }
981
+
982
+ resolve(true);
983
+ }
984
+ })
985
+ .catch((error) => {
986
+ console.log(error);
987
+ // check if review error
988
+ if (
989
+ error.response &&
990
+ error.response.request &&
991
+ error.response.request.response
992
+ ) {
993
+ const is_reviewed = JSON.parse(error.request.response).is_reviewed;
994
+ if (is_reviewed && is_reviewed.length > 0) {
995
+ const errorData = {
996
+ data: [...is_reviewed],
997
+ };
998
+ reject(errorData);
999
+ return;
1000
+ }
1001
+ }
1002
+ reject(error.response);
1003
+ });
1004
+ });
1005
+ },
1006
+
1007
+ fetchMissingAnnotations: ({ commit, state, getters }) => {
1008
+ return new Promise((resolve, reject) => {
1009
+ return HTTP.get(
1010
+ `/missing-annotations/?document=${state.documentId}&limit=100`
1011
+ )
1012
+ .then((response) => {
1013
+ commit("SET_MISSING_ANNOTATIONS", response.data.results);
1014
+ resolve(true);
1015
+ })
1016
+ .catch((error) => {
1017
+ reject(error.response);
1018
+ console.log(error);
1019
+ });
1020
+ });
1021
+ },
1022
+
1023
+ addMissingAnnotations: ({ commit, getters }, missingAnnotations) => {
1024
+ return new Promise((resolve, reject) => {
1025
+ HTTP.post(`/missing-annotations/`, missingAnnotations)
1026
+ .then(async (response) => {
1027
+ if (response.status === 201) {
1028
+ commit("SET_ANNOTATIONS_MARKED_AS_MISSING", null);
1029
+ commit("ADD_MISSING_ANNOTATIONS", response.data);
1030
+ }
1031
+
1032
+ resolve(response);
1033
+ })
1034
+ .catch((error) => {
1035
+ reject(error.response);
1036
+ console.log(error);
1037
+ });
1038
+ });
1039
+ },
1040
+
1041
+ deleteMissingAnnotation: ({ commit, getters }, id) => {
1042
+ return new Promise((resolve, reject) => {
1043
+ return HTTP.delete(`/missing-annotations/${id}/`)
1044
+ .then((response) => {
1045
+ if (response.status === 204) {
1046
+ commit("DELETE_MISSING_ANNOTATION", id);
1047
+ resolve(true);
1048
+ }
1049
+ })
1050
+ .catch((error) => {
1051
+ reject(error.response);
1052
+ console.log(error);
1053
+ });
1054
+ });
1055
+ },
1056
+
1057
+ updateMultipleAnnotations: ({ state, commit }, annotations) => {
1058
+ commit("SET_NEW_ACCEPTED_ANNOTATIONS", annotations.ids);
1059
+
1060
+ return new Promise((resolve, reject) => {
1061
+ return HTTP.patch(
1062
+ `documents/${state.documentId}/update-annotations/`,
1063
+ annotations
1064
+ )
1065
+ .then((response) => {
1066
+ if (response.status === 200) {
1067
+ response.data.map((annotation) => {
1068
+ commit("UPDATE_ANNOTATION", annotation);
1069
+ });
1070
+ commit("SET_NEW_ACCEPTED_ANNOTATIONS", null);
1071
+ resolve(true);
1072
+ }
1073
+ })
1074
+ .catch((error) => {
1075
+ console.log(error);
1076
+ reject(error.response);
1077
+ });
1078
+ });
1079
+ },
1080
+
1081
+ fetchDocumentStatus: ({ state, getters }) => {
1082
+ return new Promise((resolve, reject) => {
1083
+ return HTTP.get(
1084
+ `documents/${state.documentId}/?fields=status_data,labeling_available`
1085
+ )
1086
+ .then((response) => {
1087
+ if (
1088
+ getters.isDocumentReadyToBeReviewed(response.data) ||
1089
+ getters.waitingForSplittingConfirmation(response.data)
1090
+ ) {
1091
+ // ready or has splitting suggestions
1092
+ return resolve(true);
1093
+ } else if (getters.documentHadErrorDuringExtraction(response.data)) {
1094
+ // error
1095
+ return reject();
1096
+ } else {
1097
+ // not yet ready
1098
+ return resolve(false);
1099
+ }
1100
+ })
1101
+ .catch((error) => {
1102
+ reject(error.response);
1103
+ console.log(error);
1104
+ });
1105
+ });
1106
+ },
1107
+
1108
+ // Get document data
1109
+ fetchDocumentData: ({ state }) => {
1110
+ return new Promise((resolve, reject) => {
1111
+ HTTP.get(`documents/${state.documentId}/`)
1112
+ .then((response) => {
1113
+ return resolve(response.data);
1114
+ })
1115
+ .catch((error) => {
1116
+ reject(error);
1117
+ console.log(error);
1118
+ });
1119
+ });
1120
+ },
1121
+
1122
+ // Poll Document endpoint to know if the Document is ready to be reviewed
1123
+ // or even if there was an error during the extraction
1124
+ pollDocumentEndpoint: ({ dispatch }) => {
1125
+ return dispatch("fetchDocumentStatus")
1126
+ .then((ready) => {
1127
+ if (ready) {
1128
+ // Stop document recalculating annotations
1129
+ dispatch("endRecalculatingAnnotations");
1130
+ dispatch("fetchDocument");
1131
+ } else {
1132
+ sleep(documentPollDuration);
1133
+ dispatch("pollDocumentEndpoint");
1134
+ }
1135
+ })
1136
+ .catch((error) => {
1137
+ dispatch("setDocumentError", true);
1138
+ console.log(error);
1139
+ });
1140
+ },
1141
+
1142
+ createErrorMessage: (
1143
+ { commit, dispatch },
1144
+ { error, serverErrorMessage, defaultErrorMessage }
1145
+ ) => {
1146
+ // check type of error
1147
+ if (error && error.status >= 500 && error.status < 600) {
1148
+ dispatch("setErrorMessage", serverErrorMessage);
1149
+ commit("SET_SERVER_ERROR", true);
1150
+ } else if (error.data && error.data.length > 0) {
1151
+ dispatch("setErrorMessage", error.data[0]);
1152
+ } else {
1153
+ dispatch("setErrorMessage", defaultErrorMessage);
1154
+ }
1155
+ },
1156
+
1157
+ closeErrorMessage: ({ commit }) => {
1158
+ setTimeout(() => {
1159
+ commit("SET_ERROR_MESSAGE", null);
1160
+ commit("SET_SHOW_ACTION_ERROR", false);
1161
+ commit("SET_SERVER_ERROR", false);
1162
+ }, 5000);
1163
+ },
1164
+
1165
+ contactSupport: ({ rootState }, error) => {
1166
+ const url = "https://konfuzio.com/support/";
1167
+ const params = `project=${rootState.project.projectId}&email=${rootState.project.currentUser.username}&issue=${error}`;
1168
+ const fullUrl = `${url}?${params}`;
1169
+
1170
+ window.open(fullUrl, "_blank");
1171
+ },
1172
+
1173
+ changeCurrentDocument: ({ commit, state, dispatch }, newDocumentId) => {
1174
+ // reset splitting suggestions
1175
+ if (state.splittingSuggestions) {
1176
+ commit("SET_SPLITTING_SUGGESTIONS", null);
1177
+ }
1178
+
1179
+ if (getURLQueryParam("document") || getURLPath("d")) {
1180
+ navigateToNewDocumentURL(state.selectedDocument.id, newDocumentId);
1181
+ } else {
1182
+ commit("SET_DOC_ID", newDocumentId);
1183
+ dispatch("fetchDocument");
1184
+ }
1185
+ },
1186
+ };
1187
+
1188
+ const mutations = {
1189
+ SET_LOADING: (state, loading) => {
1190
+ state.loading = loading;
1191
+ },
1192
+ SET_DOC_ID: (state, id) => {
1193
+ if (id !== state.documentId) {
1194
+ state.documentId = id;
1195
+ }
1196
+ },
1197
+ ADD_ANNOTATION: (state, annotation) => {
1198
+ state.annotations.push(annotation);
1199
+ state.annotationSets.map((annotationSet) => {
1200
+ if (
1201
+ annotation.annotation_set === annotationSet.id &&
1202
+ annotation.label_set &&
1203
+ annotationSet.label_set.id === annotation.label_set.id
1204
+ ) {
1205
+ annotationSet.labels.map((label) => {
1206
+ if (annotation.label && annotation.label.id === label.id) {
1207
+ const exists = label.annotations.find(
1208
+ (existingAnnotation) => existingAnnotation.id === annotation.id
1209
+ );
1210
+
1211
+ if (!exists) {
1212
+ label.annotations.push(annotation);
1213
+ return;
1214
+ }
1215
+ }
1216
+ });
1217
+ }
1218
+ });
1219
+ },
1220
+ UPDATE_ANNOTATION: (state, annotation) => {
1221
+ const indexOfAnnotationInAnnotations = state.annotations.findIndex(
1222
+ (existingAnnotation) => existingAnnotation.id === annotation.id
1223
+ );
1224
+ if (indexOfAnnotationInAnnotations > -1) {
1225
+ state.annotations.splice(indexOfAnnotationInAnnotations, 1, annotation);
1226
+ }
1227
+ let updatedAnnotation = false;
1228
+ state.annotationSets.forEach((annotationSet) => {
1229
+ if (updatedAnnotation) {
1230
+ return;
1231
+ }
1232
+ annotationSet.labels.forEach((label) => {
1233
+ const indexOfAnnotationAnnotationSets = label.annotations.findIndex(
1234
+ (existingAnnotation) => existingAnnotation.id === annotation.id
1235
+ );
1236
+ if (indexOfAnnotationAnnotationSets > -1) {
1237
+ // checks if an annotation label was changed and add it to the new label
1238
+ if (annotation.label && annotation.label.id !== label.id) {
1239
+ label.annotations.splice(indexOfAnnotationAnnotationSets, 1);
1240
+
1241
+ const labelToAdd = annotationSet.labels.find(
1242
+ (labelToAdd) => labelToAdd.id === annotation.label.id
1243
+ );
1244
+ labelToAdd.annotations.push(annotation);
1245
+ } else {
1246
+ label.annotations.splice(
1247
+ indexOfAnnotationAnnotationSets,
1248
+ 1,
1249
+ annotation
1250
+ );
1251
+ }
1252
+ updatedAnnotation = true;
1253
+ return;
1254
+ }
1255
+ });
1256
+ });
1257
+ },
1258
+ DELETE_ANNOTATION: (state, annotationId) => {
1259
+ const indexOfAnnotationToDelete = state.annotations.findIndex(
1260
+ (existingAnnotation) => existingAnnotation.id === annotationId
1261
+ );
1262
+ if (indexOfAnnotationToDelete > -1) {
1263
+ state.annotations.splice(indexOfAnnotationToDelete, 1);
1264
+ }
1265
+ let deleted = false;
1266
+ state.annotationSets.forEach((annotationSet) => {
1267
+ if (deleted) {
1268
+ return;
1269
+ }
1270
+ annotationSet.labels.forEach((label) => {
1271
+ const indexOfAnnotationInLabelToDelete = label.annotations.findIndex(
1272
+ (existingAnnotation) => existingAnnotation.id === annotationId
1273
+ );
1274
+ if (indexOfAnnotationInLabelToDelete > -1) {
1275
+ label.annotations.splice(indexOfAnnotationInLabelToDelete, 1);
1276
+ deleted = true;
1277
+ return;
1278
+ }
1279
+ });
1280
+ });
1281
+ },
1282
+ SET_ANNOTATIONS: (state, annotations) => {
1283
+ state.annotations = annotations;
1284
+ },
1285
+ SET_ANNOTATION_SETS: (state, annotationSets) => {
1286
+ state.annotationSets = annotationSets;
1287
+ },
1288
+ DELETE_ANNOTATION_SET: (state, annotationSet) => {
1289
+ const indexOfSetToDelete = state.annotationSets.findIndex(
1290
+ (existingAnnotationSet) => existingAnnotationSet.id === annotationSet.id
1291
+ );
1292
+
1293
+ if (indexOfSetToDelete === -1) return;
1294
+
1295
+ state.annotationSets.splice(indexOfSetToDelete, 1);
1296
+ },
1297
+ UPDATE_ANNOTATION_SET: (state, annotationSet) => {
1298
+ const indexOfExistingAnnotationSet = state.annotationSets.findIndex(
1299
+ (existingAnnotationSet) => existingAnnotationSet.id === annotationSet.id
1300
+ );
1301
+
1302
+ if (indexOfExistingAnnotationSet === -1) return;
1303
+
1304
+ const updatedSet = { ...annotationSet, id: null };
1305
+
1306
+ state.annotationSets.splice(indexOfExistingAnnotationSet, 1, updatedSet);
1307
+ },
1308
+ SET_LABELS: (state, labels) => {
1309
+ state.labels = labels;
1310
+ },
1311
+ SET_ANNOTATION_SELECTED: (state, annotation) => {
1312
+ state.sidebarAnnotationSelected = annotation;
1313
+ },
1314
+ SET_EDIT_ANNOTATION: (state, editAnnotation) => {
1315
+ state.editAnnotation = editAnnotation;
1316
+ },
1317
+ RESET_EDIT_ANNOTATION: (state) => {
1318
+ state.editAnnotation = null;
1319
+ },
1320
+ ADD_PAGE: (state, page) => {
1321
+ // if we already have the page in the state, update it in
1322
+ // the pages array instead of creating a new one
1323
+ const existingPageIndex = state.pages.findIndex(
1324
+ (p) => p.number === page.number
1325
+ );
1326
+ if (existingPageIndex === -1) {
1327
+ state.pages.push(page);
1328
+ } else {
1329
+ state.pages[existingPageIndex] = page;
1330
+ }
1331
+ },
1332
+ SET_PAGES: (state, pages) => {
1333
+ state.pages = pages;
1334
+ },
1335
+ SET_DOCUMENT_ANNOTATION_SELECTED: (state, documentAnnotationSelected) => {
1336
+ state.documentAnnotationSelected = documentAnnotationSelected;
1337
+ },
1338
+ SET_DOCUMENT_ANNOTATION_SCROLL: (state, scrollTo) => {
1339
+ if (state.documentAnnotationSelected) {
1340
+ state.documentAnnotationSelected.scrollTo = scrollTo;
1341
+ }
1342
+ },
1343
+ SET_SELECTED_DOCUMENT: (state, document) => {
1344
+ state.selectedDocument = document;
1345
+
1346
+ // this is to handle cache when a document is edited or changed
1347
+ if (state.selectedDocument) {
1348
+ state.selectedDocument.downloaded_at = Date.now();
1349
+ }
1350
+ },
1351
+ SET_RECALCULATING_ANNOTATIONS: (state, recalculatingAnnotations) => {
1352
+ state.recalculatingAnnotations = recalculatingAnnotations;
1353
+ },
1354
+ SET_MISSING_ANNOTATIONS: (state, missingAnnotations) => {
1355
+ state.missingAnnotations = missingAnnotations;
1356
+ },
1357
+ ADD_MISSING_ANNOTATIONS: (state, annotations) => {
1358
+ if (annotations && annotations.length > 0) {
1359
+ annotations.map((annotation) => {
1360
+ // check if already in missingAnnotations
1361
+ const found = state.missingAnnotations.find(
1362
+ (missingAnnotation) => missingAnnotation.id === annotation.id
1363
+ );
1364
+
1365
+ if (found) {
1366
+ const indexOfAnnotation = state.missingAnnotations.findIndex(
1367
+ (missingAnnotation) => missingAnnotation.id === annotation.id
1368
+ );
1369
+
1370
+ if (indexOfAnnotation > -1) {
1371
+ state.missingAnnotations.splice(indexOfAnnotation, 1, annotation);
1372
+ }
1373
+ } else {
1374
+ state.missingAnnotations.push(annotation);
1375
+ }
1376
+ });
1377
+ } else {
1378
+ state.missingAnnotations.push(annotations);
1379
+ }
1380
+ },
1381
+ DELETE_MISSING_ANNOTATION: (state, id) => {
1382
+ const indexOfAnnotationToDelete = state.missingAnnotations.findIndex(
1383
+ (annotation) => annotation.id === id
1384
+ );
1385
+
1386
+ if (indexOfAnnotationToDelete > -1) {
1387
+ state.missingAnnotations.splice(indexOfAnnotationToDelete, 1);
1388
+ }
1389
+ },
1390
+ SET_SHOW_ACTION_ERROR: (state, value) => {
1391
+ state.showActionError = value;
1392
+ },
1393
+ SET_ERROR_MESSAGE: (state, message) => {
1394
+ state.errorMessage = message;
1395
+ },
1396
+ SET_DOCUMENT_ERROR: (state, value) => {
1397
+ state.showDocumentError = value;
1398
+ },
1399
+ SET_ANNOTATIONS_MARKED_AS_MISSING: (state, annotations) => {
1400
+ state.annotationsMarkedAsMissing = annotations;
1401
+ },
1402
+ SET_ERROR_MESSAGE_WIDTH: (state, width) => {
1403
+ state.errorMessageWidth = width;
1404
+ },
1405
+ SET_PUBLIC_VIEW: (state, value) => {
1406
+ state.publicView = value;
1407
+ },
1408
+ SET_DOCUMENT_IS_READY: (state, value) => {
1409
+ state.documentIsReady = value;
1410
+ },
1411
+ SET_DOCUMENT_HAS_ERROR: (state, value) => {
1412
+ state.documentHasError = value;
1413
+ },
1414
+ SET_HOVERED_ANNOTATION_SET: (state, hoveredAnnotationSet) => {
1415
+ state.hoveredAnnotationSet = hoveredAnnotationSet;
1416
+ },
1417
+ SET_NEW_ACCEPTED_ANNOTATIONS: (state, annotations) => {
1418
+ state.newAcceptedAnnotations = annotations;
1419
+ },
1420
+ SET_SERVER_ERROR: (state, value) => {
1421
+ state.serverError = value;
1422
+ },
1423
+
1424
+ UPDATE_FILE_NAME: (state, value) => {
1425
+ state.selectedDocument.data_file_name = value;
1426
+ },
1427
+ SET_SPLITTING_SUGGESTIONS: (state, array) => {
1428
+ state.splittingSuggestions = array;
1429
+ },
1430
+ };
1431
+
1432
+ export default {
1433
+ namespaced: true,
1434
+ state,
1435
+ getters,
1436
+ actions,
1437
+ mutations,
1438
+ };