@konfuzio/document-validation-ui 0.1.1

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 (131) hide show
  1. package/.eslintrc.js +11 -0
  2. package/.prettierrc.json +1 -0
  3. package/LICENSE +21 -0
  4. package/README.md +13 -0
  5. package/dist/css/app.0c8973f8.css +1 -0
  6. package/dist/css/chunk-vendors.053b6b6e.css +5 -0
  7. package/dist/favicon.ico +0 -0
  8. package/dist/index.html +1 -0
  9. package/dist/js/app.17fe48c4.js +2 -0
  10. package/dist/js/app.17fe48c4.js.map +1 -0
  11. package/dist/js/chunk-vendors.a48fca3f.js +47 -0
  12. package/dist/js/chunk-vendors.a48fca3f.js.map +1 -0
  13. package/jest.config.js +4 -0
  14. package/package.json +60 -0
  15. package/src/.DS_Store +0 -0
  16. package/src/api.js +49 -0
  17. package/src/assets/images/AcceptedCheckMark.vue +8 -0
  18. package/src/assets/images/AcceptedUser.vue +8 -0
  19. package/src/assets/images/ActionIcon.vue +60 -0
  20. package/src/assets/images/ArrowDownKey.vue +11 -0
  21. package/src/assets/images/ArrowUpKey.vue +11 -0
  22. package/src/assets/images/CategoryIconImg.vue +13 -0
  23. package/src/assets/images/CheckMark.vue +8 -0
  24. package/src/assets/images/EditDocIcon.vue +12 -0
  25. package/src/assets/images/EmptyStateImg.vue +129 -0
  26. package/src/assets/images/ErrorIcon.vue +28 -0
  27. package/src/assets/images/EyeIcon.vue +11 -0
  28. package/src/assets/images/FileNameNotSavedImage.vue +26 -0
  29. package/src/assets/images/FileNameSavedImage.vue +14 -0
  30. package/src/assets/images/FitZoomIcon.vue +16 -0
  31. package/src/assets/images/KeyboardIcon.vue +16 -0
  32. package/src/assets/images/MinusIcon.vue +13 -0
  33. package/src/assets/images/NotOptimizedIllustration.vue +651 -0
  34. package/src/assets/images/PlusIcon.vue +13 -0
  35. package/src/assets/images/QuestionMark.vue +12 -0
  36. package/src/assets/images/ServerImage.vue +63 -0
  37. package/src/assets/images/SplitLines.vue +18 -0
  38. package/src/assets/images/SplitZigZag.vue +16 -0
  39. package/src/assets/images/StatusImg.vue +14 -0
  40. package/src/assets/images/UserIcon.vue +8 -0
  41. package/src/assets/scss/annotation_details.scss +126 -0
  42. package/src/assets/scss/categorize_modal.scss +42 -0
  43. package/src/assets/scss/choose_label_set_modal.scss +62 -0
  44. package/src/assets/scss/document_action_bar.scss +37 -0
  45. package/src/assets/scss/document_annotations.scss +472 -0
  46. package/src/assets/scss/document_category.scss +80 -0
  47. package/src/assets/scss/document_dashboard.scss +47 -0
  48. package/src/assets/scss/document_dataset_status.scss +46 -0
  49. package/src/assets/scss/document_edit.scss +431 -0
  50. package/src/assets/scss/document_error.scss +81 -0
  51. package/src/assets/scss/document_handover.scss +200 -0
  52. package/src/assets/scss/document_name.scss +62 -0
  53. package/src/assets/scss/document_page.scss +8 -0
  54. package/src/assets/scss/document_thumbnails.scss +41 -0
  55. package/src/assets/scss/document_toolbar.scss +89 -0
  56. package/src/assets/scss/document_top_bar.scss +139 -0
  57. package/src/assets/scss/document_viewport_modal.scss +25 -0
  58. package/src/assets/scss/documents_list.scss +130 -0
  59. package/src/assets/scss/empty_state.scss +34 -0
  60. package/src/assets/scss/extracting_data.scss +35 -0
  61. package/src/assets/scss/imports.scss +1 -0
  62. package/src/assets/scss/main.scss +24 -0
  63. package/src/assets/scss/multi_ann_table_popup.scss +12 -0
  64. package/src/assets/scss/new_annotation.scss +86 -0
  65. package/src/assets/scss/scrolling_document.scss +19 -0
  66. package/src/assets/scss/variables.scss +696 -0
  67. package/src/components/App.vue +112 -0
  68. package/src/components/DocumentAnnotations/ActionButtons.vue +237 -0
  69. package/src/components/DocumentAnnotations/AnnotationContent.vue +249 -0
  70. package/src/components/DocumentAnnotations/AnnotationDetails.vue +292 -0
  71. package/src/components/DocumentAnnotations/AnnotationRow.vue +616 -0
  72. package/src/components/DocumentAnnotations/CategorizeModal.vue +159 -0
  73. package/src/components/DocumentAnnotations/ChooseLabelSetModal.vue +155 -0
  74. package/src/components/DocumentAnnotations/DocumentAnnotations.vue +502 -0
  75. package/src/components/DocumentAnnotations/DocumentLabel.vue +148 -0
  76. package/src/components/DocumentAnnotations/EmptyAnnotation.vue +222 -0
  77. package/src/components/DocumentAnnotations/EmptyState.vue +21 -0
  78. package/src/components/DocumentAnnotations/ExtractingData.vue +29 -0
  79. package/src/components/DocumentAnnotations/LoadingAnnotations.vue +43 -0
  80. package/src/components/DocumentAnnotations/LoadingLabels.vue +43 -0
  81. package/src/components/DocumentAnnotations/RejectedLabels.vue +96 -0
  82. package/src/components/DocumentAnnotations/index.js +8 -0
  83. package/src/components/DocumentCategory.vue +156 -0
  84. package/src/components/DocumentDashboard.vue +159 -0
  85. package/src/components/DocumentEdit/DocumentEdit.vue +279 -0
  86. package/src/components/DocumentEdit/EditPages.vue +213 -0
  87. package/src/components/DocumentEdit/EditSidebar.vue +118 -0
  88. package/src/components/DocumentEdit/SplitOverview.vue +182 -0
  89. package/src/components/DocumentEdit/index.js +4 -0
  90. package/src/components/DocumentError.vue +53 -0
  91. package/src/components/DocumentPage/ActionBar.vue +48 -0
  92. package/src/components/DocumentPage/BoxSelection.vue +149 -0
  93. package/src/components/DocumentPage/DocumentPage.vue +517 -0
  94. package/src/components/DocumentPage/DocumentToolbar.vue +145 -0
  95. package/src/components/DocumentPage/DummyPage.vue +53 -0
  96. package/src/components/DocumentPage/MultiAnnSelection.vue +302 -0
  97. package/src/components/DocumentPage/MultiAnnotationTablePopup.vue +253 -0
  98. package/src/components/DocumentPage/NewAnnotation.vue +283 -0
  99. package/src/components/DocumentPage/ScrollingDocument.vue +108 -0
  100. package/src/components/DocumentPage/ScrollingPage.vue +184 -0
  101. package/src/components/DocumentPage/index.js +5 -0
  102. package/src/components/DocumentThumbnails/DocumentThumbnails.vue +92 -0
  103. package/src/components/DocumentThumbnails/LoadingThumbnail.vue +25 -0
  104. package/src/components/DocumentThumbnails/index.js +1 -0
  105. package/src/components/DocumentTopBar/DocumentDatasetStatus.vue +103 -0
  106. package/src/components/DocumentTopBar/DocumentHandover.vue +202 -0
  107. package/src/components/DocumentTopBar/DocumentName.vue +224 -0
  108. package/src/components/DocumentTopBar/DocumentTopBar.vue +144 -0
  109. package/src/components/DocumentTopBar/DocumentTopBarButtons.vue +148 -0
  110. package/src/components/DocumentTopBar/KeyboardActionsDescription.vue +71 -0
  111. package/src/components/DocumentTopBar/index.js +5 -0
  112. package/src/components/DocumentsList/DocumentsList.vue +126 -0
  113. package/src/components/DocumentsList/index.js +1 -0
  114. package/src/components/ErrorMessage.vue +40 -0
  115. package/src/components/NotOptimizedViewportModal.vue +54 -0
  116. package/src/constants.js +4 -0
  117. package/src/directives/scroll.js +28 -0
  118. package/src/i18n.js +23 -0
  119. package/src/locales/de.json +114 -0
  120. package/src/locales/en.json +114 -0
  121. package/src/locales/es.json +113 -0
  122. package/src/main.js +87 -0
  123. package/src/store/category.js +193 -0
  124. package/src/store/display.js +238 -0
  125. package/src/store/document.js +1057 -0
  126. package/src/store/edit.js +210 -0
  127. package/src/store/index.js +22 -0
  128. package/src/store/project.js +95 -0
  129. package/src/store/selection.js +179 -0
  130. package/src/utils/utils.js +3 -0
  131. package/vue.config.js +13 -0
@@ -0,0 +1,1057 @@
1
+ import myImports from "../api";
2
+ import sleep from "../utils/utils";
3
+
4
+ const HTTP = myImports.HTTP;
5
+ const documentPollDuration = 1000;
6
+
7
+ const state = {
8
+ loading: true,
9
+ pages: [],
10
+ annotationSets: null,
11
+ annotations: null,
12
+ labels: [],
13
+ documentId: null,
14
+ sidebarAnnotationSelected: null,
15
+ documentAnnotationSelected: null,
16
+ selectedDocument: null,
17
+ recalculatingAnnotations: false,
18
+ editAnnotation: null,
19
+ missingAnnotations: [],
20
+ publicView: true,
21
+ showActionError: false,
22
+ errorMessage: null,
23
+ showDocumentError: false,
24
+ rejectedMissingAnnotations: null,
25
+ errorMessageWidth: null,
26
+ hoveredAnnotationSet: null,
27
+ finishedReview: false,
28
+ newAcceptedAnnotations: null,
29
+ selectedEntities: null,
30
+ serverError: false,
31
+ };
32
+
33
+ const getters = {
34
+ /**
35
+ * Get entities inside a box
36
+ */
37
+ entitiesOnSelection: (state) => (box, page) => {
38
+ return page.entities.filter(
39
+ (entity) =>
40
+ box.x0 <= entity.x0 &&
41
+ box.x1 >= entity.x1 &&
42
+ box.y0 <= entity.y0 &&
43
+ box.y1 >= entity.y1
44
+ );
45
+ },
46
+
47
+ /**
48
+ * Number of pages. If the pages array doesn't exist yet, return 0.
49
+ */
50
+ pageCount: (state) => {
51
+ if (state.selectedDocument.pages) {
52
+ return state.selectedDocument.pages.length;
53
+ }
54
+ return 0;
55
+ },
56
+
57
+ /**
58
+ * Returns the current page
59
+ */
60
+ pageSelected: (state, _, rootState) => {
61
+ if (state.pages) {
62
+ return state.pages[rootState.display.currentPage - 1];
63
+ }
64
+ return null;
65
+ },
66
+
67
+ /**
68
+ * Returns a page in the given index
69
+ */
70
+ pageAtIndex: (state) => (index) => {
71
+ if (state.selectedDocument && state.selectedDocument.pages) {
72
+ return state.selectedDocument.pages[index];
73
+ }
74
+ return null;
75
+ },
76
+
77
+ /**
78
+ * Checks if is to scroll to an annotation in the document
79
+ */
80
+ scrollDocumentToAnnotation: (state) => {
81
+ return (
82
+ state.documentAnnotationSelected &&
83
+ state.documentAnnotationSelected.scrollTo
84
+ );
85
+ },
86
+
87
+ /**
88
+ * Checks if the document is categorized and ready to start the review
89
+ */
90
+ categorizationIsConfirmed: (state) => {
91
+ if (state.selectedDocument) {
92
+ if (
93
+ state.selectedDocument.category_is_revised ||
94
+ state.selectedDocument.is_reviewed
95
+ ) {
96
+ return true;
97
+ } else if (!state.selectedDocument.category) {
98
+ return false;
99
+ } else {
100
+ // check if there's any annotation already approved
101
+ const found = state.annotations.find((annotation) => {
102
+ return annotation.revised;
103
+ });
104
+ return found != undefined;
105
+ }
106
+ }
107
+ return false;
108
+ },
109
+
110
+ /**
111
+ * Gets labels for an annotation creation from a label/annotation set
112
+ */
113
+ labelsFilteredForAnnotationCreation: (state) => (set) => {
114
+ let returnLabels = [];
115
+ if (set.id && set.labels) {
116
+ // if existing ann set, check for multiple
117
+ returnLabels = set.labels.filter((label) => {
118
+ // check if label has multiple and if not, if there's already an annotation created
119
+ if (!label.has_multiple_top_candidates) {
120
+ const existingLabel = state.labels.find((documentLabel) => {
121
+ return documentLabel.id === label.id;
122
+ });
123
+ return (
124
+ existingLabel &&
125
+ existingLabel.annotations &&
126
+ existingLabel.annotations.length === 0
127
+ );
128
+ } else {
129
+ return true;
130
+ }
131
+ });
132
+ } else if (set.labels) {
133
+ // if not existing ann set, then return all labels
134
+ returnLabels = set.labels;
135
+ }
136
+ return returnLabels;
137
+ },
138
+
139
+ /* Checks if annotation is in deleted state */
140
+ isAnnotationDeleted: (state) => (annotation) => {
141
+ if (annotation) {
142
+ return annotation.revised && !annotation.is_correct;
143
+ }
144
+ return false;
145
+ },
146
+
147
+ /* Checks if the label has annotations to show */
148
+ labelHasAnnotations: (_, getters) => (label) => {
149
+ const annotations = label.annotations.filter((annotation) => {
150
+ return !getters.isAnnotationDeleted(annotation);
151
+ });
152
+ return annotations.length > 0;
153
+ },
154
+
155
+ /* Checks if the document has an annotation set */
156
+ annotationSetExists: (state) => (annotationSetId) => {
157
+ return state.annotationSets.find((annSet) => annSet.id === annotationSetId);
158
+ },
159
+
160
+ /* Process annotations and extract labels and sets */
161
+ processAnnotationSets: (state, getters) => (annotationSets) => {
162
+ // group annotations for sidebar
163
+ const annotations = [];
164
+ const labels = [];
165
+ const processedAnnotationSets = annotationSets.map((annotationSet) => {
166
+ const annotationSetLabels = annotationSet.labels.map((label) => {
167
+ // filter label
168
+ const filteredLabel = getters.annotationsInLabelFiltered(label);
169
+
170
+ // add annotations to the document array
171
+ annotations.push(...filteredLabel.annotations);
172
+ labels.push(filteredLabel);
173
+ // add labels to the labels array
174
+ return filteredLabel;
175
+ });
176
+ annotationSet.labels = annotationSetLabels;
177
+ return annotationSet;
178
+ });
179
+
180
+ return {
181
+ annotationSets: processedAnnotationSets,
182
+ labels,
183
+ annotations,
184
+ };
185
+ },
186
+
187
+ /* Returns the number of accepted annotations in a label */
188
+ numberOfAcceptedAnnotationsInLabel: (_) => (label) => {
189
+ const annotations = label.annotations.filter((annotation) => {
190
+ return annotation.revised && annotation.is_correct;
191
+ });
192
+ return annotations.length;
193
+ },
194
+
195
+ /**
196
+ * Checks if theres a group of annotation sets and add an index number to them
197
+ */
198
+ numberOfAnnotationSetGroup: (state) => (annotationSet) => {
199
+ let found = false;
200
+ let value = 0;
201
+ let index = 0;
202
+ if (state.annotationSets) {
203
+ state.annotationSets.map((annotationSetTemp) => {
204
+ if (
205
+ annotationSetTemp.id !== annotationSet.id &&
206
+ annotationSetTemp.label_set.id === annotationSet.label_set.id &&
207
+ annotationSetTemp.label_set.name === annotationSet.label_set.name
208
+ ) {
209
+ found = true;
210
+ index++;
211
+ } else if (
212
+ annotationSetTemp.label_set.id === annotationSet.label_set.id
213
+ ) {
214
+ value = index;
215
+ }
216
+ });
217
+ return found ? `${value + 1}` : "";
218
+ }
219
+ },
220
+
221
+ /**
222
+ * Get label with annotations filtered if the label supports multiple or not
223
+ */
224
+ annotationsInLabelFiltered: (state) => (label) => {
225
+ let labelToReturn;
226
+ if (
227
+ label.has_multiple_top_candidates === false &&
228
+ label.annotations &&
229
+ label.annotations.length > 1
230
+ ) {
231
+ let highestConfidenceAnnotation = label.annotations[0];
232
+ for (let i = 1; i < label.annotations.length; i++) {
233
+ // check which one has more confidence or if it's the same, then check if one is revised or not
234
+ if (
235
+ highestConfidenceAnnotation.confidence <
236
+ label.annotations[i].confidence ||
237
+ (highestConfidenceAnnotation.confidence ===
238
+ label.annotations[i].confidence &&
239
+ label.annotations[i].revised)
240
+ ) {
241
+ highestConfidenceAnnotation = label.annotations[i];
242
+ }
243
+ }
244
+ labelToReturn = {
245
+ ...label,
246
+ annotations: [highestConfidenceAnnotation],
247
+ };
248
+ } else {
249
+ labelToReturn = {
250
+ ...label,
251
+ };
252
+ }
253
+ return labelToReturn;
254
+ },
255
+
256
+ /**
257
+ * Checks if annotation is being edited
258
+ */
259
+ isAnnotationInEditMode:
260
+ (state) =>
261
+ (annotationId, index = null) => {
262
+ if (state.editAnnotation && annotationId) {
263
+ if (index != null) {
264
+ return (
265
+ state.editAnnotation.id === annotationId &&
266
+ state.editAnnotation.index === index
267
+ );
268
+ }
269
+ return state.editAnnotation.id === annotationId;
270
+ }
271
+ },
272
+
273
+ /**
274
+ * Get number of empty labels per annotation set
275
+ */
276
+ emptyLabelsLength: (state) => (annotationSet) => {
277
+ const labels = annotationSet.labels.filter(
278
+ (label) => label.annotations.length === 0
279
+ );
280
+
281
+ const pendingEmpty = [];
282
+
283
+ labels.map((label) => {
284
+ const found = state.missingAnnotations.find(
285
+ (l) => l.label === label.id && annotationSet.id === l.annotation_set
286
+ );
287
+
288
+ if (!found) {
289
+ pendingEmpty.push(label);
290
+ }
291
+ });
292
+
293
+ return pendingEmpty.length;
294
+ },
295
+
296
+ // Check if document is ready to be finished
297
+ isDocumentReviewFinished: (state) => () => {
298
+ // check if all annotations have been revised
299
+ let notRevised;
300
+
301
+ const emptyAnnotations = [];
302
+
303
+ if (state.annotationSets) {
304
+ state.annotationSets.forEach((annSet) => {
305
+ annSet.labels.map((label) => {
306
+ // return only labels with empty annotations
307
+ if (label.annotations.length === 0) {
308
+ emptyAnnotations.push({
309
+ label: label.id,
310
+ label_set: annSet.label_set.id,
311
+ });
312
+ }
313
+ });
314
+ });
315
+ }
316
+
317
+ if (state.annotations) {
318
+ notRevised = state.annotations.filter((a) => !a.revised);
319
+ }
320
+
321
+ // if all annotations have been revised
322
+ // and if there are no empty annotations or
323
+ // all empty annotations were rejected,
324
+ // we can finish the review
325
+ if (
326
+ !emptyAnnotations ||
327
+ !state.missingAnnotations ||
328
+ !notRevised ||
329
+ (notRevised.length === 0 &&
330
+ state.missingAnnotations.length === emptyAnnotations.length)
331
+ ) {
332
+ return true;
333
+ }
334
+
335
+ return false;
336
+ },
337
+
338
+ /**
339
+ * Get number of annotations pending review per annotation set
340
+ */
341
+ annotationsWithPendingReviewLength: () => (annotationSet) => {
342
+ const labels = annotationSet.labels.filter(
343
+ (label) => label.annotations.length > 0
344
+ );
345
+
346
+ const annotationsWithPendingReview = [];
347
+
348
+ labels.map((label) => {
349
+ const foundPendingAnnotation = label.annotations.find(
350
+ (ann) => !ann.revised
351
+ );
352
+
353
+ if (foundPendingAnnotation) {
354
+ annotationsWithPendingReview.push(foundPendingAnnotation);
355
+ }
356
+ });
357
+
358
+ // Check if we have grouped annotations by same label
359
+ if (state.enableGroupingFeature && label.annotations.length < 2) {
360
+ return annotationsWithPendingReview.length - label.annotations.length;
361
+ }
362
+
363
+ return annotationsWithPendingReview.length;
364
+ },
365
+
366
+ /**
367
+ * Check if the document was extracted correctly and is ready to be reviewed
368
+ */
369
+ isDocumentReadyToBeReviewed: () => (document) => {
370
+ return document.status_data === 2 && document.labeling_available === 1;
371
+ },
372
+
373
+ /**
374
+ * Check if the document had an error during extraction
375
+ */
376
+ documentHadErrorDuringExtraction: () => (document) => {
377
+ return document.status_data === 111;
378
+ },
379
+
380
+ /**
381
+ * check if the document has a dataset status of 'Training', 'Test' or 'Preparation'
382
+ * or if it is Ready Only / Reviewed
383
+ * and if so disable the option to edit the document
384
+ */
385
+ documentCannotBeEdited: (state) => (document) => {
386
+ return (
387
+ document.dataset_status === 1 ||
388
+ document.dataset_status === 2 ||
389
+ document.dataset_status === 3 ||
390
+ document.is_reviewed ||
391
+ state.publicView
392
+ );
393
+ },
394
+
395
+ getTextFromEntities: (state) => () => {
396
+ return state.selectedEntities
397
+ .map((entity) => {
398
+ return entity.content;
399
+ })
400
+ .join(" ");
401
+ },
402
+ };
403
+
404
+ const actions = {
405
+ startLoading: ({ commit }) => {
406
+ commit("SET_LOADING", true);
407
+ },
408
+ endLoading: ({ commit }) => {
409
+ commit("SET_LOADING", false);
410
+ },
411
+ setDocId: ({ commit }, id) => {
412
+ commit("SET_PAGES", []);
413
+ commit("SET_DOC_ID", id);
414
+ },
415
+ setSidebarAnnotationSelected: ({ commit }, annotation) => {
416
+ commit("SET_ANNOTATION_SELECTED", annotation);
417
+ },
418
+ setAnnotationSets: ({ commit }, annotationSets) => {
419
+ commit("SET_ANNOTATION_SETS", annotationSets);
420
+ },
421
+ setEditAnnotation: (
422
+ { commit },
423
+ { id, index, label, labelSet, annotationSet }
424
+ ) => {
425
+ const value = {
426
+ id,
427
+ index,
428
+ label,
429
+ labelSet,
430
+ annotationSet,
431
+ };
432
+ commit("SET_EDIT_ANNOTATION", value);
433
+ },
434
+ resetEditAnnotation: ({ commit }) => {
435
+ commit("RESET_EDIT_ANNOTATION");
436
+ },
437
+ setAnnotations: ({ commit }, annotations) => {
438
+ commit("SET_ANNOTATIONS", annotations);
439
+ },
440
+ setLabels: ({ commit }, labels) => {
441
+ commit("SET_LABELS", labels);
442
+ },
443
+ setPages: ({ commit }, pages) => {
444
+ commit("SET_PAGES", pages);
445
+ },
446
+ setSelectedDocument: ({ commit }, document) => {
447
+ commit("SET_SELECTED_DOCUMENT", document);
448
+ },
449
+ setPublicView: ({ commit }, publicView) => {
450
+ commit("SET_PUBLIC_VIEW", publicView);
451
+ },
452
+ startRecalculatingAnnotations: ({ commit }) => {
453
+ commit("SET_RECALCULATING_ANNOTATIONS", true);
454
+ },
455
+ endRecalculatingAnnotations: ({ commit }) => {
456
+ commit("SET_RECALCULATING_ANNOTATIONS", false);
457
+ },
458
+ setMissingAnnotations: ({ commit }, missingAnnotations) => {
459
+ commit("SET_MISSING_ANNOTATIONS", missingAnnotations);
460
+ },
461
+ setErrorMessage: ({ commit, dispatch }, message) => {
462
+ if (message) {
463
+ commit("SET_SHOW_ACTION_ERROR", true);
464
+ } else {
465
+ commit("SET_SHOW_ACTION_ERROR", false);
466
+ }
467
+
468
+ commit("SET_ERROR_MESSAGE", message);
469
+
470
+ dispatch("closeErrorMessage");
471
+ },
472
+ setDocumentError: ({ commit }, value) => {
473
+ commit("SET_DOCUMENT_ERROR", value);
474
+ },
475
+ setRejectedMissingAnnotations: ({ commit }, annotations) => {
476
+ commit("SET_REJECTED_MISSING_ANNOTATIONS", annotations);
477
+ },
478
+ setErrorMessageWidth: ({ commit }, width) => {
479
+ commit("SET_ERROR_MESSAGE_WIDTH", width);
480
+ },
481
+ setHoveredAnnotationSet: ({ commit }, annotationSet) => {
482
+ commit("SET_HOVERED_ANNOTATION_SET", annotationSet);
483
+ },
484
+ setNewAcceptedAnnotations: ({ commit }, annotations) => {
485
+ commit("SET_NEW_ACCEPTED_ANNOTATIONS", annotations);
486
+ },
487
+ setSelectedEntities: ({ commit }, entities) => {
488
+ commit("SET_SELECTED_ENTITIES", entities);
489
+ },
490
+
491
+ /**
492
+ * Actions that use HTTP requests always return the axios promise,
493
+ * so they can be `await`ed (useful to set the `loading` status).
494
+ */
495
+ fetchDocument: async (
496
+ { commit, state, dispatch, rootState, getters },
497
+ pollDocumentList = false
498
+ ) => {
499
+ let projectId = null;
500
+ let categoryId = null;
501
+ let isRecalculatingAnnotations = false;
502
+
503
+ const initialPage = 1;
504
+
505
+ dispatch("startLoading");
506
+ dispatch("display/updateCurrentPage", initialPage, {
507
+ root: true,
508
+ });
509
+
510
+ await HTTP.get(`documents/${state.documentId}/`)
511
+ .then(async (response) => {
512
+ if (response.data) {
513
+ const { labels, annotations, annotationSets } =
514
+ getters.processAnnotationSets(response.data.annotation_sets);
515
+
516
+ // load first page
517
+ if (response.data.pages.length > 0) {
518
+ await dispatch("fetchDocumentPage", initialPage);
519
+ }
520
+ // set information on the store
521
+ commit("SET_ANNOTATION_SETS", annotationSets);
522
+ commit("SET_ANNOTATIONS", annotations);
523
+ commit("SET_LABELS", labels);
524
+ commit("SET_SELECTED_DOCUMENT", response.data);
525
+ commit("SET_FINISHED_REVIEW", getters.isDocumentReviewFinished());
526
+
527
+ if (rootState.project.projectId) {
528
+ projectId = rootState.project.projectId;
529
+ } else {
530
+ projectId = response.data.project;
531
+ dispatch("project/setProjectId", response.data.project, {
532
+ root: true,
533
+ });
534
+ }
535
+
536
+ categoryId = response.data.category;
537
+ // TODO: add this validation to a method
538
+ isRecalculatingAnnotations = response.data.labeling_available !== 1;
539
+ }
540
+ })
541
+ .catch((error) => {
542
+ console.log(error, "Could not fetch document details from the backend");
543
+ return;
544
+ });
545
+
546
+ await dispatch("fetchMissingAnnotations");
547
+
548
+ if (!state.publicView) {
549
+ await dispatch("project/fetchCurrentUser", null, {
550
+ root: true,
551
+ });
552
+
553
+ if (projectId) {
554
+ await dispatch("category/fetchCategories", projectId, {
555
+ root: true,
556
+ });
557
+ }
558
+ if (categoryId) {
559
+ await dispatch(
560
+ "category/createAvailableDocumentsList",
561
+ {
562
+ categoryId,
563
+ user: rootState.project.currentUser,
564
+ poll: pollDocumentList,
565
+ },
566
+ {
567
+ root: true,
568
+ }
569
+ );
570
+ }
571
+ }
572
+ if (isRecalculatingAnnotations) {
573
+ commit("SET_RECALCULATING_ANNOTATIONS", true);
574
+ dispatch("pollDocumentEndpoint");
575
+ }
576
+ dispatch("endLoading");
577
+ },
578
+
579
+ // Get document page data
580
+ fetchDocumentPage: ({ commit, state }, page) => {
581
+ return HTTP.get(`documents/${state.documentId}/pages/${page}/`)
582
+ .then((response) => {
583
+ commit("ADD_PAGE", response.data);
584
+ })
585
+ .catch((error) => {
586
+ console.log(error);
587
+ });
588
+ },
589
+
590
+ setDocumentAnnotationSelected: (
591
+ { commit },
592
+ { annotation, label, span, scrollTo = false }
593
+ ) => {
594
+ const value = {
595
+ scrollTo,
596
+ id: annotation.id,
597
+ span,
598
+ page: span.page_index + 1,
599
+ labelName: label.name,
600
+ };
601
+ commit("SET_DOCUMENT_ANNOTATION_SELECTED", value);
602
+ },
603
+
604
+ scrollToDocumentAnnotationSelected: ({ commit }) => {
605
+ commit("SET_DOCUMENT_ANNOTATION_SCROLL", true);
606
+ },
607
+
608
+ disableDocumentAnnotationSelected: ({ commit }) => {
609
+ commit("SET_DOCUMENT_ANNOTATION_SELECTED", null);
610
+ },
611
+
612
+ createAnnotation: ({ commit, getters, dispatch }, annotation) => {
613
+ return new Promise((resolve, reject) => {
614
+ HTTP.post(`/annotations/`, annotation)
615
+ .then(async (response) => {
616
+ if (response.status === 201) {
617
+ dispatch("fetchMissingAnnotations");
618
+ commit("SET_FINISHED_REVIEW", getters.isDocumentReviewFinished());
619
+
620
+ if (!getters.annotationSetExists(response.data.annotation_set)) {
621
+ const documentData = await dispatch("fetchDocumentData");
622
+ if (documentData && documentData.annotation_sets) {
623
+ const { labels, annotations, annotationSets } =
624
+ getters.processAnnotationSets(documentData.annotation_sets);
625
+ commit("SET_ANNOTATION_SETS", annotationSets);
626
+ commit("SET_ANNOTATIONS", annotations);
627
+ commit("SET_LABELS", labels);
628
+ }
629
+ } else {
630
+ commit("ADD_ANNOTATION", response.data);
631
+ }
632
+ resolve(response);
633
+ }
634
+ })
635
+ .catch((error) => {
636
+ reject(error.response);
637
+ console.log(error);
638
+ });
639
+ });
640
+ },
641
+
642
+ updateAnnotation: ({ commit, getters }, { updatedValues, annotationId }) => {
643
+ commit("SET_NEW_ACCEPTED_ANNOTATIONS", [annotationId]);
644
+
645
+ return new Promise((resolve, reject) => {
646
+ HTTP.patch(`/annotations/${annotationId}/`, updatedValues)
647
+ .then((response) => {
648
+ if (response.status === 200) {
649
+ commit("UPDATE_ANNOTATION", response.data);
650
+ commit("SET_FINISHED_REVIEW", getters.isDocumentReviewFinished());
651
+ commit("SET_NEW_ACCEPTED_ANNOTATIONS", null);
652
+ resolve(true);
653
+ }
654
+ })
655
+ .catch((error) => {
656
+ reject(error.response);
657
+ console.log(error);
658
+ });
659
+ });
660
+ },
661
+
662
+ deleteAnnotation: ({ commit, getters }, { annotationId }) => {
663
+ return new Promise((resolve, reject) => {
664
+ HTTP.delete(`/annotations/${annotationId}/`)
665
+ .then((response) => {
666
+ if (response.status === 204) {
667
+ commit("DELETE_ANNOTATION", annotationId);
668
+ commit("SET_FINISHED_REVIEW", getters.isDocumentReviewFinished());
669
+ resolve(true);
670
+ }
671
+ })
672
+ .catch((error) => {
673
+ reject(error.response);
674
+ console.log(error);
675
+ });
676
+ });
677
+ },
678
+
679
+ updateDocument: ({ commit, state, getters, dispatch }, updatedDocument) => {
680
+ return new Promise((resolve, reject) => {
681
+ HTTP.patch(`/documents/${state.documentId}/`, updatedDocument)
682
+ .then((response) => {
683
+ if (response.status === 200) {
684
+ commit("SET_SELECTED_DOCUMENT", response.data);
685
+ commit("SET_FINISHED_REVIEW", getters.isDocumentReviewFinished());
686
+
687
+ dispatch("pollDocumentEndpoint");
688
+
689
+ resolve(true);
690
+ }
691
+ })
692
+ .catch((error) => {
693
+ reject(error.response);
694
+ console.log(error);
695
+ });
696
+ });
697
+ },
698
+
699
+ fetchMissingAnnotations: ({ commit, state, getters }) => {
700
+ return HTTP.get(
701
+ `/missing-annotations/?document=${state.documentId}&limit=100`
702
+ )
703
+ .then((response) => {
704
+ commit("SET_MISSING_ANNOTATIONS", response.data.results);
705
+ commit("SET_FINISHED_REVIEW", getters.isDocumentReviewFinished());
706
+ })
707
+ .catch((error) => {
708
+ console.log(error);
709
+ });
710
+ },
711
+
712
+ addMissingAnnotations: ({ commit, dispatch }, missingAnnotations) => {
713
+ return new Promise((resolve, reject) => {
714
+ return HTTP.post(`/missing-annotations/`, missingAnnotations)
715
+ .then((response) => {
716
+ if (response.status === 201) {
717
+ commit("SET_REJECTED_MISSING_ANNOTATIONS", null);
718
+ dispatch("fetchMissingAnnotations");
719
+ }
720
+
721
+ resolve(response);
722
+ })
723
+ .catch((error) => {
724
+ reject(error.response);
725
+ console.log(error);
726
+ });
727
+ });
728
+ },
729
+
730
+ deleteMissingAnnotation: ({ commit, getters, dispatch }, id) => {
731
+ return new Promise((resolve) => {
732
+ return HTTP.delete(`/missing-annotations/${id}/`)
733
+ .then((response) => {
734
+ if (response.status === 204) {
735
+ dispatch("fetchMissingAnnotations");
736
+ resolve(true);
737
+ }
738
+ })
739
+ .catch((error) => {
740
+ resolve(error.response);
741
+ console.log(error);
742
+ });
743
+ });
744
+ },
745
+
746
+ updateMultipleAnnotations: ({ state, commit }, annotations) => {
747
+ commit("SET_NEW_ACCEPTED_ANNOTATIONS", annotations.ids);
748
+
749
+ return new Promise((resolve, reject) => {
750
+ return HTTP.patch(
751
+ `documents/${state.documentId}/update-annotations/`,
752
+ annotations
753
+ )
754
+ .then((response) => {
755
+ if (response.status === 200) {
756
+ response.data.map((annotation) => {
757
+ commit("UPDATE_ANNOTATION", annotation);
758
+ });
759
+ commit("SET_NEW_ACCEPTED_ANNOTATIONS", null);
760
+ resolve(true);
761
+ }
762
+ })
763
+ .catch((error) => {
764
+ console.log(error);
765
+ reject(error.response);
766
+ });
767
+ });
768
+ },
769
+
770
+ fetchDocumentStatus: ({ state, getters }) => {
771
+ return new Promise((resolve, reject) => {
772
+ return HTTP.get(
773
+ `documents/${state.documentId}/?fields=status_data,labeling_available`
774
+ )
775
+ .then((response) => {
776
+ if (getters.isDocumentReadyToBeReviewed(response.data)) {
777
+ // ready
778
+ return resolve(true);
779
+ } else if (getters.documentHadErrorDuringExtraction(response.data)) {
780
+ // error
781
+ return reject();
782
+ } else {
783
+ // not yet ready
784
+ return resolve(false);
785
+ }
786
+ })
787
+ .catch((error) => {
788
+ console.log(error);
789
+ });
790
+ });
791
+ },
792
+
793
+ // Get document data
794
+ fetchDocumentData: ({ state }) => {
795
+ return new Promise((resolve, reject) => {
796
+ HTTP.get(`documents/${state.documentId}/`)
797
+ .then((response) => {
798
+ return resolve(response.data);
799
+ })
800
+ .catch((error) => {
801
+ reject(error);
802
+ console.log(error);
803
+ });
804
+ });
805
+ },
806
+
807
+ // Poll Document endpoint to know if the Document is ready to be reviewed
808
+ // or even if there was an error during the extraction
809
+ pollDocumentEndpoint: ({ dispatch }) => {
810
+ return dispatch("fetchDocumentStatus")
811
+ .then((ready) => {
812
+ if (ready) {
813
+ // Stop document recalculating annotations
814
+ dispatch("endRecalculatingAnnotations");
815
+ dispatch("fetchDocument");
816
+ } else {
817
+ sleep(documentPollDuration);
818
+ dispatch("pollDocumentEndpoint");
819
+ }
820
+ })
821
+ .catch((error) => {
822
+ dispatch("setDocumentError", true);
823
+ console.log(error);
824
+ });
825
+ },
826
+
827
+ createErrorMessage: (
828
+ { commit, dispatch },
829
+ { error, serverErrorMessage, defaultErrorMessage }
830
+ ) => {
831
+ let errorAsString;
832
+
833
+ if (error && error.status) {
834
+ errorAsString = error.status.toString();
835
+ }
836
+
837
+ // check type of error
838
+ if (error.data && error.data.length > 0) {
839
+ dispatch("setErrorMessage", error.data[0]);
840
+ } else if (errorAsString.startsWith("5")) {
841
+ dispatch("setErrorMessage", serverErrorMessage);
842
+ commit("SET_SERVER_ERROR", true);
843
+ } else {
844
+ dispatch("setErrorMessage", defaultErrorMessage);
845
+ }
846
+ },
847
+
848
+ closeErrorMessage: ({ commit }) => {
849
+ setTimeout(() => {
850
+ commit("SET_ERROR_MESSAGE", null);
851
+ commit("SET_SHOW_ACTION_ERROR", false);
852
+ commit("SET_SERVER_ERROR", false);
853
+ }, 5000);
854
+ },
855
+
856
+ contactSupport: ({ rootState }, error) => {
857
+ let url;
858
+ const params = `project=${rootState.project.projectId}&email=${rootState.project.currentUser}&issue=${error}`;
859
+
860
+ if (process.env.VUE_APP_I18N_LOCALE == "de") {
861
+ url = "https://konfuzio.com/de/support/";
862
+ } else {
863
+ url = "https://konfuzio.com/en/support/";
864
+ }
865
+
866
+ window.open(`${url}?${params}`, "_blank");
867
+ },
868
+ };
869
+
870
+ const mutations = {
871
+ SET_LOADING: (state, loading) => {
872
+ state.loading = loading;
873
+ },
874
+ SET_DOC_ID: (state, id) => {
875
+ if (id !== state.documentId) {
876
+ state.documentId = id;
877
+ }
878
+ },
879
+ ADD_ANNOTATION: (state, annotation) => {
880
+ state.annotations.push(annotation);
881
+ state.annotationSets.map((annotationSet) => {
882
+ if (
883
+ annotation.annotation_set === annotationSet.id &&
884
+ annotation.label_set &&
885
+ annotationSet.label_set.id === annotation.label_set.id
886
+ ) {
887
+ annotationSet.labels.map((label) => {
888
+ if (annotation.label && annotation.label.id === label.id) {
889
+ const exists = label.annotations.find(
890
+ (existingAnnotation) => existingAnnotation.id === annotation.id
891
+ );
892
+ if (!exists) {
893
+ label.annotations.push(annotation);
894
+ return;
895
+ }
896
+ }
897
+ });
898
+ }
899
+ });
900
+ },
901
+ UPDATE_ANNOTATION: (state, annotation) => {
902
+ const indexOfAnnotationInAnnotations = state.annotations.findIndex(
903
+ (existingAnnotation) => existingAnnotation.id === annotation.id
904
+ );
905
+ if (indexOfAnnotationInAnnotations > -1) {
906
+ state.annotations[indexOfAnnotationInAnnotations] = annotation;
907
+ }
908
+ let updatedAnnotation = false;
909
+ state.annotationSets.forEach((annotationSet) => {
910
+ if (updatedAnnotation) {
911
+ return;
912
+ }
913
+ annotationSet.labels.forEach((label) => {
914
+ const indexOfAnnotationAnnotationSets = label.annotations.findIndex(
915
+ (existingAnnotation) => existingAnnotation.id === annotation.id
916
+ );
917
+ if (indexOfAnnotationAnnotationSets > -1) {
918
+ label.annotations.splice(
919
+ indexOfAnnotationAnnotationSets,
920
+ 1,
921
+ annotation
922
+ );
923
+ updatedAnnotation = true;
924
+ return;
925
+ }
926
+ });
927
+ });
928
+ },
929
+ DELETE_ANNOTATION: (state, annotationId) => {
930
+ const indexOfAnnotationToDelete = state.annotations.findIndex(
931
+ (existingAnnotation) => existingAnnotation.id === annotationId
932
+ );
933
+ if (indexOfAnnotationToDelete > -1) {
934
+ state.annotations.splice(indexOfAnnotationToDelete, 1);
935
+ }
936
+ let deleted = false;
937
+ state.annotationSets.forEach((annotationSet) => {
938
+ if (deleted) {
939
+ return;
940
+ }
941
+ annotationSet.labels.forEach((label) => {
942
+ const indexOfAnnotationInLabelToDelete = label.annotations.findIndex(
943
+ (existingAnnotation) => existingAnnotation.id === annotationId
944
+ );
945
+ if (indexOfAnnotationInLabelToDelete > -1) {
946
+ label.annotations.splice(indexOfAnnotationInLabelToDelete, 1);
947
+ deleted = true;
948
+ return;
949
+ }
950
+ });
951
+ });
952
+ },
953
+ SET_ANNOTATIONS: (state, annotations) => {
954
+ state.annotations = annotations;
955
+ },
956
+ SET_ANNOTATION_SETS: (state, annotationSets) => {
957
+ state.annotationSets = annotationSets;
958
+ },
959
+ SET_LABELS: (state, labels) => {
960
+ state.labels = labels;
961
+ },
962
+ SET_ANNOTATION_SELECTED: (state, annotation) => {
963
+ state.sidebarAnnotationSelected = annotation;
964
+ },
965
+ SET_EDIT_ANNOTATION: (state, editAnnotation) => {
966
+ state.editAnnotation = editAnnotation;
967
+ },
968
+ SET_FINISHED_REVIEW: (state, finishedReview) => {
969
+ state.finishedReview = finishedReview;
970
+ },
971
+ RESET_EDIT_ANNOTATION: (state) => {
972
+ state.editAnnotation = null;
973
+ },
974
+ ADD_PAGE: (state, page) => {
975
+ // if we already have the page in the state, update it in
976
+ // the pages array instead of creating a new one
977
+ const existingPageIndex = state.pages.findIndex(
978
+ (p) => p.number === page.number
979
+ );
980
+ if (existingPageIndex === -1) {
981
+ state.pages.push(page);
982
+ } else {
983
+ state.pages[existingPageIndex] = page;
984
+ }
985
+ },
986
+ SET_PAGES: (state, pages) => {
987
+ state.pages = pages;
988
+ },
989
+ SET_DOCUMENT_ANNOTATION_SELECTED: (state, documentAnnotationSelected) => {
990
+ state.documentAnnotationSelected = documentAnnotationSelected;
991
+ },
992
+ SET_DOCUMENT_ANNOTATION_SCROLL: (state, scrollTo) => {
993
+ if (state.documentAnnotationSelected) {
994
+ state.documentAnnotationSelected.scrollTo = scrollTo;
995
+ }
996
+ },
997
+ SET_SELECTED_DOCUMENT: (state, document) => {
998
+ if (document.is_reviewed === true) {
999
+ state.publicView = true;
1000
+ }
1001
+ state.selectedDocument = document;
1002
+
1003
+ // this is to handle cache when a document is edited or changed
1004
+ state.selectedDocument.downloaded_at = Date.now();
1005
+ },
1006
+ SET_RECALCULATING_ANNOTATIONS: (state, recalculatingAnnotations) => {
1007
+ state.recalculatingAnnotations = recalculatingAnnotations;
1008
+ },
1009
+ SET_MISSING_ANNOTATIONS: (state, missingAnnotations) => {
1010
+ state.missingAnnotations = missingAnnotations;
1011
+ },
1012
+
1013
+ SET_SHOW_ACTION_ERROR: (state, value) => {
1014
+ state.showActionError = value;
1015
+ },
1016
+ SET_ERROR_MESSAGE: (state, message) => {
1017
+ state.errorMessage = message;
1018
+ },
1019
+ SET_DOCUMENT_ERROR: (state, value) => {
1020
+ state.showDocumentError = value;
1021
+ },
1022
+ SET_REJECTED_MISSING_ANNOTATIONS: (state, annotations) => {
1023
+ state.rejectedMissingAnnotations = annotations;
1024
+ },
1025
+ SET_ERROR_MESSAGE_WIDTH: (state, width) => {
1026
+ state.errorMessageWidth = width;
1027
+ },
1028
+ SET_PUBLIC_VIEW: (state, value) => {
1029
+ state.publicView = value;
1030
+ },
1031
+ SET_DOCUMENT_IS_READY: (state, value) => {
1032
+ state.documentIsReady = value;
1033
+ },
1034
+ SET_DOCUMENT_HAS_ERROR: (state, value) => {
1035
+ state.documentHasError = value;
1036
+ },
1037
+ SET_HOVERED_ANNOTATION_SET: (state, hoveredAnnotationSet) => {
1038
+ state.hoveredAnnotationSet = hoveredAnnotationSet;
1039
+ },
1040
+ SET_NEW_ACCEPTED_ANNOTATIONS: (state, annotations) => {
1041
+ state.newAcceptedAnnotations = annotations;
1042
+ },
1043
+ SET_SELECTED_ENTITIES: (state, entities) => {
1044
+ state.selectedEntities = entities;
1045
+ },
1046
+ SET_SERVER_ERROR: (state, value) => {
1047
+ state.serverError = value;
1048
+ },
1049
+ };
1050
+
1051
+ export default {
1052
+ namespaced: true,
1053
+ state,
1054
+ getters,
1055
+ actions,
1056
+ mutations,
1057
+ };