@konfuzio/document-validation-ui 0.2.0-dev.1 → 0.2.0-dev.2

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.
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@konfuzio/document-validation-ui",
3
- "version": "0.2.0-dev.1",
3
+ "version": "0.2.0-dev.2",
4
4
  "repository": "https://github.com/konfuzio-ai/document-validation-ui",
5
5
  "main": "dist/app.js",
6
6
  "scripts": {
package/src/api.js CHANGED
@@ -75,6 +75,18 @@ const makeFileRequest = (fileUrl) => {
75
75
  resolve(myBlob);
76
76
  })
77
77
  .catch((error) => {
78
+ if (error.response.status === 428) {
79
+ // retry request after 2 seconds
80
+ setTimeout(() => {
81
+ makeFileRequest(fileUrl)
82
+ .then((response) => {
83
+ resolve(response);
84
+ })
85
+ .catch((error) => {
86
+ reject(error);
87
+ });
88
+ }, 2000);
89
+ }
78
90
  reject(error);
79
91
  });
80
92
  });
@@ -4,6 +4,23 @@
4
4
  align-items: center;
5
5
  gap: 6px;
6
6
 
7
+ .ann-nav-btns {
8
+ display: flex;
9
+ flex-direction: column;
10
+ color: variables.$grey-blue;
11
+ font-size: 14px;
12
+ text-align: center;
13
+ padding-right: 8px;
14
+
15
+ .button-icon {
16
+ width: 12px !important;
17
+ height: 12px !important;
18
+ &.is-link {
19
+ cursor: pointer !important;
20
+ }
21
+ }
22
+ }
23
+
7
24
  .button-action {
8
25
  padding: 0px;
9
26
  vertical-align: middle;
@@ -44,6 +61,10 @@
44
61
  width: 16px;
45
62
  height: 16px;
46
63
  }
64
+ &.angle-icon {
65
+ width: 12px;
66
+ height: 12px;
67
+ }
47
68
  }
48
69
 
49
70
  .button-text {
@@ -66,6 +87,7 @@
66
87
  .search-icon,
67
88
  .missing-icon,
68
89
  .link-icon,
90
+ .angle-icon,
69
91
  .spinner-icon {
70
92
  color: variables.$grey-blue;
71
93
  }
@@ -3,11 +3,9 @@
3
3
  #document-annotations {
4
4
  font-family: variables.$font-family;
5
5
  background-color: variables.$background;
6
- overflow-y: auto;
7
6
  position: relative;
8
7
  display: flex;
9
8
  flex-direction: column;
10
- height: 100%;
11
9
 
12
10
  &.disabled {
13
11
  pointer-events: none;
@@ -217,7 +215,7 @@
217
215
  bottom: 0px;
218
216
  left: 4px;
219
217
  right: 16px;
220
- padding: 0 20%;
218
+ margin: 0 20%;
221
219
  display: flex;
222
220
  align-items: center;
223
221
  border-radius: 8px;
@@ -379,17 +377,17 @@
379
377
  outline: none;
380
378
  }
381
379
 
382
- .label-empty {
383
- font-weight: 500;
384
- font-size: 14px;
385
- color: variables.$primary;
386
- }
387
-
388
380
  .label-empty-clicked {
389
381
  user-select: auto;
390
382
  font-weight: 400;
391
383
  color: variables.$text-lighter;
392
384
  }
385
+
386
+ .label-empty {
387
+ font-weight: 500;
388
+ font-size: 14px;
389
+ color: variables.$primary;
390
+ }
393
391
  }
394
392
  }
395
393
 
@@ -23,6 +23,11 @@
23
23
  background-size: 9px 9px;
24
24
  }
25
25
 
26
+ .document-sidebar {
27
+ overflow-y: auto;
28
+ background-color: variables.$background;
29
+ }
30
+
26
31
  @media print {
27
32
  .dashboard-document {
28
33
  position: static;
@@ -1,5 +1,22 @@
1
1
  <template>
2
2
  <div class="action-buttons">
3
+ <!-- label multi false nav buttons -->
4
+ <b-tooltip
5
+ v-if="isLabelMultiFalseAndGroupOfAnns(label) && !showSave"
6
+ :delay="tooltipDelay"
7
+ position="is-left"
8
+ :label="$t('nav_label_anns')"
9
+ >
10
+ <div class="ann-nav-btns">
11
+ <span>{{ label.annotations.length }}</span>
12
+ <b-icon
13
+ icon="angle-down"
14
+ class="angle-icon center-icon button-icon is-link"
15
+ @click.stop="nextAnn"
16
+ />
17
+ </div>
18
+ </b-tooltip>
19
+
3
20
  <!-- link button -->
4
21
  <b-button
5
22
  v-if="showLink"
@@ -211,7 +228,10 @@ export default {
211
228
  },
212
229
  computed: {
213
230
  ...mapState("document", ["publicView"]),
214
- ...mapGetters("document", ["isDocumentReviewed"]),
231
+ ...mapGetters("document", [
232
+ "isDocumentReviewed",
233
+ "isLabelMultiFalseAndGroupOfAnns",
234
+ ]),
215
235
  showHoverButton() {
216
236
  return (
217
237
  !this.isLoading &&
@@ -257,6 +277,12 @@ export default {
257
277
  link() {
258
278
  this.$emit("link");
259
279
  },
280
+ nextAnn() {
281
+ this.$store.dispatch(
282
+ "document/putNextAnnotationInLabelFirst",
283
+ this.label
284
+ );
285
+ },
260
286
  },
261
287
  };
262
288
  </script>
@@ -171,6 +171,7 @@
171
171
  >
172
172
  <AnnotationActionButtons
173
173
  :annotation="annotation"
174
+ :label="label"
174
175
  :show-cancel="showCancelButton()"
175
176
  :show-accept="showAcceptButton()"
176
177
  :show-decline="showDeclineButton()"
@@ -257,7 +258,6 @@ export default {
257
258
  "editAnnotation",
258
259
  "annotationId",
259
260
  "hoveredAnnotationSet",
260
- "enableGroupingFeature",
261
261
  "publicView",
262
262
  "newAcceptedAnnotations",
263
263
  "annotationsMarkedAsMissing",
@@ -691,12 +691,13 @@ export default {
691
691
 
692
692
  // check if annotation set was changed
693
693
  if (
694
- (this.editAnnotation.annotationSet &&
694
+ !isToDecline &&
695
+ ((this.editAnnotation.annotationSet &&
695
696
  (this.editAnnotation.annotationSet.id !== this.annotationSet.id ||
696
697
  (this.editAnnotation.annotationSet.id == null &&
697
698
  this.labelSet.id !== this.editAnnotation.labelSet.id))) ||
698
- (this.editAnnotation.label &&
699
- this.editAnnotation.label.id !== this.label.id)
699
+ (this.editAnnotation.label &&
700
+ this.editAnnotation.label.id !== this.label.id))
700
701
  ) {
701
702
  // first delete annotation, then create new one
702
703
  this.$store
@@ -1,43 +1,8 @@
1
1
  <template>
2
2
  <div class="label">
3
- <div
4
- v-if="isGroup"
5
- :class="['label-group', !showAnnotationsGroup && 'keyboard-nav']"
6
- @click.stop="toggleGroup"
7
- >
8
- <div class="label-group-left">
9
- <b-icon
10
- :icon="showAnnotationsGroup ? 'angle-up' : 'angle-down'"
11
- size="is-16"
12
- class="caret"
13
- />
14
- <div class="label-name">
15
- <span>{{ `${label.name} (${label.annotations.length})` }}</span>
16
- </div>
17
- </div>
18
- <div class="label-group-right">
19
- <div v-if="!publicView" class="label-annotations-pending">
20
- {{
21
- `${label.annotations.length - acceptedAnnotationsGroupCounter} ${$t(
22
- "annotations_pending"
23
- )}`
24
- }}
25
- </div>
26
- <div v-if="!publicView" class="label-annotations-accepted">
27
- {{
28
- `${acceptedAnnotationsGroupCounter} ${$t("annotations_accepted")}`
29
- }}
30
- </div>
31
- </div>
32
- </div>
33
- <div
34
- v-if="showAnnotationsGroup && annotationSet"
35
- :class="isGroup && 'label-group-annotation-list'"
36
- >
3
+ <div v-if="annotationSet">
37
4
  <AnnotationRow
38
- v-for="(annotation, index) in hasAnnotations
39
- ? label.annotations
40
- : [singleAnnotation]"
5
+ v-for="(annotation, index) in annotationsToShow()"
41
6
  :key="index"
42
7
  :annotation="annotation"
43
8
  :label="label"
@@ -45,7 +10,7 @@
45
10
  :label-set="annotationSet.label_set"
46
11
  />
47
12
  </div>
48
- <div v-else-if="showAnnotationsGroup && labelSet">
13
+ <div v-else-if="labelSet">
49
14
  <AnnotationRow :label="label" :label-set="labelSet" />
50
15
  </div>
51
16
  </div>
@@ -74,94 +39,19 @@ export default {
74
39
  required: true,
75
40
  },
76
41
  },
77
- data() {
78
- return {
79
- nonMultipleAnnotationsExtracted: false,
80
- acceptedAnnotationsGroupCounter: 0,
81
- showAnnotationsGroup: false,
82
- };
83
- },
84
42
  computed: {
85
- ...mapState("document", [
86
- "annotationId",
87
- "enableGroupingFeature",
88
- "hoveredAnnotationSet",
89
- "publicView",
43
+ ...mapState("document", ["annotationId", "publicView"]),
44
+ ...mapGetters("document", [
45
+ "numberOfAcceptedAnnotationsInLabel",
46
+ "isLabelMultiFalseAndGroupOfAnns",
90
47
  ]),
91
- ...mapGetters("document", ["numberOfAcceptedAnnotationsInLabel"]),
92
- singleAnnotation() {
93
- if (this.label.annotations && this.label.annotations.length > 0) {
94
- return this.label.annotations[0];
95
- }
96
- return null;
97
- },
98
- hasAnnotations() {
99
- return this.label.annotations && this.label.annotations.length > 0;
100
- },
101
- isGroup() {
102
- return this.enableGroupingFeature && this.nonMultipleAnnotationsExtracted;
103
- },
104
- },
105
- watch: {
106
- annotationId(newAnnotationId) {
107
- this.checkAnnotationSelected(newAnnotationId);
108
- },
109
- hoveredAnnotationSet(newValue) {
110
- // Check if there are some unrevised Annotations within the group
111
- if (
112
- newValue &&
113
- newValue.type === "accept" &&
114
- this.labelHasPendingAnnotations(newValue)
115
- ) {
116
- this.showAnnotationsGroup = true;
117
- }
118
- },
119
- },
120
- mounted() {
121
- this.updateValues();
122
- this.checkAnnotationSelected(this.annotationId);
123
-
124
- if (this.publicView) {
125
- this.showAnnotationsGroup = true;
126
- }
127
- },
128
- updated() {
129
- this.updateValues();
130
48
  },
131
49
  methods: {
132
- checkAnnotationSelected(newAnnotationId) {
133
- // check if annotation is inside a label group and open it
134
- if (
135
- this.enableGroupingFeature &&
136
- !this.showAnnotationsGroup &&
137
- newAnnotationId
138
- ) {
139
- const annotation = this.label.annotations.find(
140
- (ann) => ann.id == newAnnotationId
141
- );
142
-
143
- if (annotation) {
144
- this.showAnnotationsGroup = true;
145
- }
146
- }
147
- },
148
- toggleGroup() {
149
- this.showAnnotationsGroup = !this.showAnnotationsGroup;
150
- },
151
- updateValues() {
152
- // more than 1 Annotation extracted for a non multiple Label
153
- this.nonMultipleAnnotationsExtracted =
154
- this.label.annotations &&
155
- this.label.annotations.length > 1 &&
156
- !this.label.has_multiple_top_candidates;
157
-
158
- if (this.nonMultipleAnnotationsExtracted) {
159
- this.acceptedAnnotationsGroupCounter =
160
- this.numberOfAcceptedAnnotationsInLabel(this.label);
161
- }
162
- if (!this.isGroup) {
163
- // if not a group then show by default
164
- this.showAnnotationsGroup = true;
50
+ annotationsToShow() {
51
+ if (this.isLabelMultiFalseAndGroupOfAnns(this.label)) {
52
+ return [this.label.annotations[0]];
53
+ } else {
54
+ return this.label.annotations;
165
55
  }
166
56
  },
167
57
  labelHasPendingAnnotations(hoveredSet) {
@@ -10,7 +10,7 @@
10
10
  class="dashboard-document"
11
11
  />
12
12
  </pane>
13
- <pane :size="50">
13
+ <pane :size="50" class="document-sidebar">
14
14
  <DocumentAnnotations v-if="!editMode" ref="annotations" />
15
15
  <DocumentEdit v-else ref="editView" />
16
16
  </pane>
@@ -59,11 +59,6 @@
59
59
  />
60
60
  <div class="switch-info">
61
61
  <span class="switch-text">{{ $t("smart_split") }}</span>
62
- <span
63
- v-if="documentHasProposedSplit(selectedDocument)"
64
- class="new-badge"
65
- >{{ newText }}</span
66
- >
67
62
  </div>
68
63
  </b-field>
69
64
  </b-tooltip>
@@ -93,7 +88,6 @@ export default {
93
88
  return {
94
89
  buttonDisabled: true,
95
90
  tooltipInfo: null,
96
- newText: this.$t("new"),
97
91
  switchStatus: true,
98
92
  };
99
93
  },
@@ -129,9 +123,6 @@ export default {
129
123
  this.$nextTick(() => {
130
124
  this.switchStatus = this.splitSuggestionsEnabled;
131
125
  this.tooltipInfo = this.$t("no_splitting_suggestions");
132
- if (this.newText) {
133
- this.newText = this.$t("new").toUpperCase();
134
- }
135
126
  });
136
127
  },
137
128
  methods: {
@@ -17,6 +17,7 @@
17
17
  v-model="textFromEntities"
18
18
  class="popup-input"
19
19
  type="text"
20
+ :disabled="true"
20
21
  />
21
22
  </div>
22
23
  <b-dropdown
@@ -166,5 +166,6 @@
166
166
  "checkbox_ann_details": "Für dieses Label wird ein Kästchen extrahiert.",
167
167
  "document_section": "Dokumentabschnitt",
168
168
  "label_size": "Spaltengröße:",
169
- "powered_by": "betrieben von Konfuzio"
169
+ "powered_by": "betrieben von Konfuzio",
170
+ "nav_label_anns": "Durch Annotationen navigieren"
170
171
  }
@@ -167,5 +167,6 @@
167
167
  "checkbox_ann_details": "A checkbox will be extracted for this label.",
168
168
  "document_section": "Document Section",
169
169
  "label_size": "Column size:",
170
- "powered_by": "powered by Konfuzio"
170
+ "powered_by": "powered by Konfuzio",
171
+ "nav_label_anns": "Navigate through annotations"
171
172
  }
@@ -166,5 +166,6 @@
166
166
  "checkbox_ann_details": "Se extraerá una casilla de verificación para esta etiqueta.",
167
167
  "document_section": "Sección del documento",
168
168
  "label_size": "Tamaño de columna:",
169
- "powered_by": "impulsado por Konfuzio"
169
+ "powered_by": "impulsado por Konfuzio",
170
+ "nav_label_anns": "Navegar por las anotaciones"
170
171
  }
@@ -153,6 +153,31 @@ const getters = {
153
153
  return annotations.length > 0;
154
154
  },
155
155
 
156
+ /* Checks if the label has annotations to show */
157
+ isLabelMultiFalseAndGroupOfAnns: (state) => (label) => {
158
+ return (
159
+ label &&
160
+ label.annotations &&
161
+ label.annotations.length > 1 &&
162
+ !label.has_multiple_top_candidates &&
163
+ state.enableGroupingFeature
164
+ );
165
+ },
166
+
167
+ /* Returns the annotations ordered by highest confidence */
168
+ annotationsByConfidence: (state) => (annotations) => {
169
+ annotations.sort((a, b) => {
170
+ if (a.confidence < b.confidence) {
171
+ return -1;
172
+ } else if (a.confidence > b.confidence) {
173
+ return 1;
174
+ }
175
+ return 0;
176
+ });
177
+
178
+ return annotations;
179
+ },
180
+
156
181
  /* Checks if the document has an annotation set */
157
182
  annotationSetExists: (state) => (annotationSetId) => {
158
183
  return state.annotationSets.find((annSet) => annSet.id === annotationSetId);
@@ -256,11 +281,16 @@ const getters = {
256
281
  /* Get label for a given annotation */
257
282
  labelOfAnnotation: (state) => (annotationToFind) => {
258
283
  let foundLabel = null;
259
- state.annotationSets.forEach((annotationSet) => {
260
- annotationSet.labels.forEach((label) => {
261
- label.annotations.forEach((annotation) => {
262
- if (annotation.id === annotationToFind.id) {
263
- foundLabel = label;
284
+ if (state.annotationSets) {
285
+ state.annotationSets.forEach((annotationSet) => {
286
+ annotationSet.labels.forEach((label) => {
287
+ label.annotations.forEach((annotation) => {
288
+ if (annotation.id === annotationToFind.id) {
289
+ foundLabel = label;
290
+ return;
291
+ }
292
+ });
293
+ if (foundLabel) {
264
294
  return;
265
295
  }
266
296
  });
@@ -268,14 +298,12 @@ const getters = {
268
298
  return;
269
299
  }
270
300
  });
271
- if (foundLabel) {
272
- return;
273
- }
274
- });
301
+ }
302
+
275
303
  return foundLabel;
276
304
  },
277
305
 
278
- getAnnotationsFiltered: (state) => {
306
+ getAnnotationsFiltered: (state, getters) => {
279
307
  // group annotations for sidebar
280
308
  let annotations = [];
281
309
  let labels = [];
@@ -331,6 +359,18 @@ const getters = {
331
359
  return false;
332
360
  };
333
361
 
362
+ const sortByConfidenceOrByAnnotationSelected = (annotations) => {
363
+ annotations = getters.annotationsByConfidence(annotations);
364
+ if (state.annotationId) {
365
+ for (let i = 0; i < annotations.length; i++) {
366
+ if (state.annotationId == annotations[i].id) {
367
+ annotations.unshift(annotations.splice(i, 1)[0]);
368
+ }
369
+ }
370
+ }
371
+ return annotations;
372
+ };
373
+
334
374
  if (state.annotationSets) {
335
375
  state.annotationSets.forEach((annotationSet) => {
336
376
  labels = [];
@@ -350,6 +390,13 @@ const getters = {
350
390
  !state.annotationFilters.showFeedbackNeeded ||
351
391
  !state.annotationFilters.showAccepted
352
392
  ) {
393
+ if (!label.has_multiple_top_candidates) {
394
+ // if multi label = false, sort by confidence
395
+ label.annotations = sortByConfidenceOrByAnnotationSelected(
396
+ label.annotations
397
+ );
398
+ }
399
+
353
400
  label.annotations.forEach((annotation) => {
354
401
  if (
355
402
  state.annotationFilters.showFeedbackNeeded &&
@@ -379,6 +426,12 @@ const getters = {
379
426
  }
380
427
  });
381
428
  } else {
429
+ if (!label.has_multiple_top_candidates) {
430
+ // if multi label = false, sort by confidence
431
+ label.annotations = sortByConfidenceOrByAnnotationSelected(
432
+ label.annotations
433
+ );
434
+ }
382
435
  // add annotations to the document array
383
436
  label.annotations.forEach((annotation) => {
384
437
  const added = addAnnotation(
@@ -963,6 +1016,15 @@ const actions = {
963
1016
  commit("SET_DOC_ID", id);
964
1017
  },
965
1018
  setAnnotationId: ({ commit, dispatch, getters }, id) => {
1019
+ if (id) {
1020
+ // check if part of label with multi ann as false
1021
+ const annotation = getters.annotationById(id);
1022
+ const label = getters.labelOfAnnotation(annotation);
1023
+ if (getters.isLabelMultiFalseAndGroupOfAnns(label)) {
1024
+ dispatch("setAnnotationAsFirstInLabel", { label, annotation });
1025
+ }
1026
+ }
1027
+
966
1028
  commit("SET_ANNOTATION_ID", id);
967
1029
  setURLAnnotationHash(id);
968
1030
  },
@@ -1542,6 +1604,57 @@ const actions = {
1542
1604
  showAcceptedAnnotations({ commit }, show) {
1543
1605
  commit("SET_SHOW_ACCEPTED_ANNOTATIONS", show);
1544
1606
  },
1607
+ setAnnotationAsFirstInLabel({ commit, state }, { label, annotation }) {
1608
+ state.annotationSets.forEach((annotationSet) => {
1609
+ annotationSet.labels.forEach((labelToFind) => {
1610
+ if (labelToFind.id === label.id) {
1611
+ if (labelToFind.annotations && labelToFind.annotations.length > 1) {
1612
+ for (let i = 0; i < labelToFind.annotations.length; i++) {
1613
+ if (labelToFind.annotations[i].id === annotation.id) {
1614
+ labelToFind.annotations.unshift(
1615
+ labelToFind.annotations.splice(i, 1)[0]
1616
+ );
1617
+ commit("SET_ANNOTATIONS_IN_LABEL", {
1618
+ label: labelToFind,
1619
+ annotations: labelToFind.annotations,
1620
+ });
1621
+ }
1622
+ }
1623
+ }
1624
+ }
1625
+ });
1626
+ });
1627
+ },
1628
+ putNextAnnotationInLabelFirst({ commit, state, dispatch }, label) {
1629
+ dispatch("setAnnotationId", null);
1630
+ let newFirstAnn = null;
1631
+ state.annotationSets.forEach((annotationSet) => {
1632
+ annotationSet.labels.forEach((labelToFind) => {
1633
+ if (labelToFind.id === label.id) {
1634
+ if (labelToFind.annotations && labelToFind.annotations.length > 1) {
1635
+ const firstElement = labelToFind.annotations.shift();
1636
+ labelToFind.annotations.push(firstElement);
1637
+ commit("SET_ANNOTATIONS_IN_LABEL", {
1638
+ label: labelToFind,
1639
+ annotations: labelToFind.annotations,
1640
+ });
1641
+ newFirstAnn = labelToFind.annotations[0];
1642
+ }
1643
+ }
1644
+ });
1645
+ });
1646
+
1647
+ if (newFirstAnn) {
1648
+ dispatch("setDocumentAnnotationSelected", {
1649
+ annotation: newFirstAnn,
1650
+ label: label,
1651
+ span: newFirstAnn.span[0],
1652
+ scrollTo: true,
1653
+ });
1654
+
1655
+ dispatch("scrollToDocumentAnnotationSelected");
1656
+ }
1657
+ },
1545
1658
  };
1546
1659
 
1547
1660
  const mutations = {
@@ -1687,6 +1800,13 @@ const mutations = {
1687
1800
  SET_LABELS: (state, labels) => {
1688
1801
  state.labels = labels;
1689
1802
  },
1803
+ SET_ANNOTATIONS_IN_LABEL: (state, { label, annotations }) => {
1804
+ state.labels.forEach((labelToFind) => {
1805
+ if (labelToFind.id === label.id) {
1806
+ labelToFind.annotations = annotations;
1807
+ }
1808
+ });
1809
+ },
1690
1810
  SET_EDIT_ANNOTATION: (state, editAnnotation) => {
1691
1811
  state.editAnnotation = editAnnotation;
1692
1812
  },