Added handling for recommendations
This commit is contained in:
parent
5c06b6b6e5
commit
dd53e652fe
@ -1,8 +1,19 @@
|
|||||||
<div [class.visible]="menuOpen" *ngIf="canPerformAnnotationActions" class="annotation-actions">
|
<div [class.visible]="menuOpen" *ngIf="canPerformAnnotationActions" class="annotation-actions">
|
||||||
|
<redaction-circle-button
|
||||||
|
(action)="convertRecommendationToAnnotation($event, annotation)"
|
||||||
|
type="dark-bg"
|
||||||
|
*ngIf="canConvertRecommendationToAnnotation"
|
||||||
|
tooltipPosition="before"
|
||||||
|
tooltip="annotation-actions.accept-recommendation.label"
|
||||||
|
icon="red:check-alt"
|
||||||
|
>
|
||||||
|
</redaction-circle-button>
|
||||||
|
|
||||||
<redaction-circle-button
|
<redaction-circle-button
|
||||||
(action)="acceptSuggestion($event, annotation)"
|
(action)="acceptSuggestion($event, annotation)"
|
||||||
type="dark-bg"
|
type="dark-bg"
|
||||||
*ngIf="canAcceptSuggestion"
|
*ngIf="canAcceptSuggestion"
|
||||||
|
tooltipPosition="before"
|
||||||
tooltip="annotation-actions.accept-suggestion.label"
|
tooltip="annotation-actions.accept-suggestion.label"
|
||||||
icon="red:check-alt"
|
icon="red:check-alt"
|
||||||
>
|
>
|
||||||
@ -13,6 +24,7 @@
|
|||||||
*ngIf="canUndoAnnotation"
|
*ngIf="canUndoAnnotation"
|
||||||
type="dark-bg"
|
type="dark-bg"
|
||||||
icon="red:undo"
|
icon="red:undo"
|
||||||
|
tooltipPosition="before"
|
||||||
tooltip="annotation-actions.undo"
|
tooltip="annotation-actions.undo"
|
||||||
>
|
>
|
||||||
</redaction-circle-button>
|
</redaction-circle-button>
|
||||||
@ -22,6 +34,7 @@
|
|||||||
type="dark-bg"
|
type="dark-bg"
|
||||||
icon="red:close"
|
icon="red:close"
|
||||||
*ngIf="canRejectSuggestion"
|
*ngIf="canRejectSuggestion"
|
||||||
|
tooltipPosition="before"
|
||||||
tooltip="annotation-actions.reject-suggestion"
|
tooltip="annotation-actions.reject-suggestion"
|
||||||
>
|
>
|
||||||
</redaction-circle-button>
|
</redaction-circle-button>
|
||||||
@ -31,6 +44,7 @@
|
|||||||
type="dark-bg"
|
type="dark-bg"
|
||||||
icon="red:trash"
|
icon="red:trash"
|
||||||
*ngIf="canDirectlySuggestToRemoveAnnotation"
|
*ngIf="canDirectlySuggestToRemoveAnnotation"
|
||||||
|
tooltipPosition="before"
|
||||||
tooltip="annotation-actions.suggest-remove-annotation"
|
tooltip="annotation-actions.suggest-remove-annotation"
|
||||||
>
|
>
|
||||||
</redaction-circle-button>
|
</redaction-circle-button>
|
||||||
@ -40,6 +54,7 @@
|
|||||||
(action)="openMenu($event)"
|
(action)="openMenu($event)"
|
||||||
[class.active]="menuOpen"
|
[class.active]="menuOpen"
|
||||||
[matMenuTriggerFor]="menu"
|
[matMenuTriggerFor]="menu"
|
||||||
|
tooltipPosition="before"
|
||||||
tooltip="annotation-actions.suggest-remove-annotation"
|
tooltip="annotation-actions.suggest-remove-annotation"
|
||||||
type="dark-bg"
|
type="dark-bg"
|
||||||
icon="red:trash"
|
icon="red:trash"
|
||||||
|
|||||||
@ -13,6 +13,10 @@
|
|||||||
padding-top: 8px;
|
padding-top: 8px;
|
||||||
background: linear-gradient(to right, rgba(255, 255, 255, 0) 0%, #f9fafb, #f9fafb, #f9fafb);
|
background: linear-gradient(to right, rgba(255, 255, 255, 0) 0%, #f9fafb, #f9fafb, #f9fafb);
|
||||||
|
|
||||||
|
redaction-circle-button {
|
||||||
|
display: block;
|
||||||
|
}
|
||||||
|
|
||||||
.confirm.active {
|
.confirm.active {
|
||||||
background-color: $grey-2;
|
background-color: $grey-2;
|
||||||
}
|
}
|
||||||
|
|||||||
@ -16,7 +16,7 @@ export class AnnotationActionsComponent implements OnInit {
|
|||||||
@Input() annotation: AnnotationWrapper;
|
@Input() annotation: AnnotationWrapper;
|
||||||
@Input() canPerformAnnotationActions: boolean;
|
@Input() canPerformAnnotationActions: boolean;
|
||||||
|
|
||||||
@Output() annotationsChanged = new EventEmitter();
|
@Output() annotationsChanged = new EventEmitter<boolean>();
|
||||||
|
|
||||||
suggestionType: TypeValue;
|
suggestionType: TypeValue;
|
||||||
menuOpen: boolean;
|
menuOpen: boolean;
|
||||||
@ -46,11 +46,18 @@ export class AnnotationActionsComponent implements OnInit {
|
|||||||
}
|
}
|
||||||
|
|
||||||
get canDirectlySuggestToRemoveAnnotation() {
|
get canDirectlySuggestToRemoveAnnotation() {
|
||||||
return this.annotation.isHint || (this.annotation.isManual && this.permissionsService.isManagerAndOwner() && !this.canUndoAnnotation);
|
return (
|
||||||
|
(this.annotation.isHint || (this.annotation.isManual && this.permissionsService.isManagerAndOwner() && !this.canUndoAnnotation)) &&
|
||||||
|
!this.annotation.isRecommendation
|
||||||
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
get requiresSuggestionRemoveMenu() {
|
get requiresSuggestionRemoveMenu() {
|
||||||
return this.annotation.isRedacted || this.annotation.isIgnored;
|
return (this.annotation.isRedacted || this.annotation.isIgnored) && !this.annotation.isRecommendation;
|
||||||
|
}
|
||||||
|
|
||||||
|
get canConvertRecommendationToAnnotation() {
|
||||||
|
return this.annotation.isRecommendation;
|
||||||
}
|
}
|
||||||
|
|
||||||
get canUndoAnnotation() {
|
get canUndoAnnotation() {
|
||||||
@ -84,8 +91,8 @@ export class AnnotationActionsComponent implements OnInit {
|
|||||||
|
|
||||||
private _processObsAndEmit(obs: Observable<any>) {
|
private _processObsAndEmit(obs: Observable<any>) {
|
||||||
obs.subscribe(
|
obs.subscribe(
|
||||||
() => {
|
(data) => {
|
||||||
this.annotationsChanged.emit();
|
this.annotationsChanged.emit(!!data?.annotationId);
|
||||||
},
|
},
|
||||||
() => {
|
() => {
|
||||||
this.annotationsChanged.emit();
|
this.annotationsChanged.emit();
|
||||||
@ -109,4 +116,9 @@ export class AnnotationActionsComponent implements OnInit {
|
|||||||
get dictionaryColor() {
|
get dictionaryColor() {
|
||||||
return this.appStateService.getDictionaryColor('suggestion-add-dictionary');
|
return this.appStateService.getDictionaryColor('suggestion-add-dictionary');
|
||||||
}
|
}
|
||||||
|
|
||||||
|
convertRecommendationToAnnotation($event: any, annotation: AnnotationWrapper) {
|
||||||
|
$event.stopPropagation();
|
||||||
|
this._processObsAndEmit(this._manualAnnotationService.addRecommendation(annotation));
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@ -200,7 +200,7 @@
|
|||||||
</div>
|
</div>
|
||||||
<redaction-comments [annotation]="annotation"></redaction-comments>
|
<redaction-comments [annotation]="annotation"></redaction-comments>
|
||||||
<redaction-annotation-actions
|
<redaction-annotation-actions
|
||||||
(annotationsChanged)="annotationsChangedByReviewAction(annotation)"
|
(annotationsChanged)="annotationsChangedByReviewAction(true, annotation)"
|
||||||
[annotation]="annotation"
|
[annotation]="annotation"
|
||||||
[canPerformAnnotationActions]="canPerformAnnotationActions"
|
[canPerformAnnotationActions]="canPerformAnnotationActions"
|
||||||
></redaction-annotation-actions>
|
></redaction-annotation-actions>
|
||||||
|
|||||||
@ -441,13 +441,41 @@ export class FilePreviewScreenComponent implements OnInit, OnDestroy {
|
|||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
async annotationsChangedByReviewAction(annotation: AnnotationWrapper) {
|
private async _cleanupAndRedrawManualAnnotationsForEntirePage(page: number) {
|
||||||
const viewerAnnotation = this.activeViewer.annotManager.getAnnotationById(annotation.id);
|
const currentPageAnnotationIds = this.displayedAnnotations[page].annotations.map((a) => a.id);
|
||||||
|
this.fileData.fileStatus = await this.appStateService.reloadActiveFile();
|
||||||
|
|
||||||
|
this._fileDownloadService.loadActiveFileManualAnnotations().subscribe((manualRedactions) => {
|
||||||
|
this.fileData.manualRedactions = manualRedactions;
|
||||||
|
this._rebuildFilters();
|
||||||
|
if (!this.redactedView) {
|
||||||
|
currentPageAnnotationIds.forEach((id) => {
|
||||||
|
this._findAndDeleteAnnotation(id);
|
||||||
|
});
|
||||||
|
this._annotationDrawService.drawAnnotations(
|
||||||
|
this.instance,
|
||||||
|
this.annotations.filter((item) => item.pageNumber === page)
|
||||||
|
);
|
||||||
|
document.querySelectorAll('iframe')[0].click();
|
||||||
|
}
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
async annotationsChangedByReviewAction(requiresCompletePageRedraw: boolean, annotation: AnnotationWrapper) {
|
||||||
|
if (!requiresCompletePageRedraw) {
|
||||||
|
this._findAndDeleteAnnotation(annotation.id);
|
||||||
|
this.fileData.fileStatus = await this.appStateService.reloadActiveFile();
|
||||||
|
this._cleanupAndRedrawManualAnnotations(annotation.id);
|
||||||
|
} else {
|
||||||
|
await this._cleanupAndRedrawManualAnnotationsForEntirePage(annotation.pageNumber);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private _findAndDeleteAnnotation(id: string) {
|
||||||
|
const viewerAnnotation = this.activeViewer.annotManager.getAnnotationById(id);
|
||||||
if (viewerAnnotation) {
|
if (viewerAnnotation) {
|
||||||
this.activeViewer.annotManager.deleteAnnotation(viewerAnnotation, true, true);
|
this.activeViewer.annotManager.deleteAnnotation(viewerAnnotation, true, true);
|
||||||
}
|
}
|
||||||
this.fileData.fileStatus = await this.appStateService.reloadActiveFile();
|
|
||||||
this._cleanupAndRedrawManualAnnotations(annotation.id);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
async fileActionPerformed(action: string) {
|
async fileActionPerformed(action: string) {
|
||||||
@ -496,5 +524,6 @@ export class FilePreviewScreenComponent implements OnInit, OnDestroy {
|
|||||||
async openHTMLDebug() {
|
async openHTMLDebug() {
|
||||||
window.open(`/html-debug/${this.fileId}`, '_blank');
|
window.open(`/html-debug/${this.fileId}`, '_blank');
|
||||||
}
|
}
|
||||||
|
|
||||||
// <!-- End Dev Mode Features-->
|
// <!-- End Dev Mode Features-->
|
||||||
}
|
}
|
||||||
|
|||||||
@ -1,7 +1,6 @@
|
|||||||
import { Comment, IdRemoval, ManualRedactionEntry, Point, Rectangle, RedactionLogEntry, TypeValue } from '@redaction/red-ui-http';
|
import { Comment, IdRemoval, ManualRedactionEntry, Point, Rectangle, RedactionLogEntry, TypeValue } from '@redaction/red-ui-http';
|
||||||
import { UserWrapper } from '../../../user/user.service';
|
import { UserWrapper } from '../../../user/user.service';
|
||||||
import { FileStatusWrapper } from './file-status.wrapper';
|
import { FileStatusWrapper } from './file-status.wrapper';
|
||||||
import { humanize } from '../../../utils/functions';
|
|
||||||
|
|
||||||
export class AnnotationWrapper {
|
export class AnnotationWrapper {
|
||||||
superType:
|
superType:
|
||||||
@ -31,7 +30,9 @@ export class AnnotationWrapper {
|
|||||||
redaction: boolean;
|
redaction: boolean;
|
||||||
status: string;
|
status: string;
|
||||||
dictionaryOperation: boolean;
|
dictionaryOperation: boolean;
|
||||||
|
recommendation: boolean;
|
||||||
positions: Rectangle[];
|
positions: Rectangle[];
|
||||||
|
recommendationType: string;
|
||||||
|
|
||||||
get isIgnored() {
|
get isIgnored() {
|
||||||
return this.superType === 'ignore';
|
return this.superType === 'ignore';
|
||||||
@ -81,6 +82,10 @@ export class AnnotationWrapper {
|
|||||||
return this.dictionaryOperation;
|
return this.dictionaryOperation;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
get isRecommendation() {
|
||||||
|
return this.recommendation;
|
||||||
|
}
|
||||||
|
|
||||||
static fromData(
|
static fromData(
|
||||||
user: UserWrapper,
|
user: UserWrapper,
|
||||||
dictionaryData: { [p: string]: TypeValue },
|
dictionaryData: { [p: string]: TypeValue },
|
||||||
@ -107,6 +112,14 @@ export class AnnotationWrapper {
|
|||||||
|
|
||||||
const annotationWrapper = new AnnotationWrapper();
|
const annotationWrapper = new AnnotationWrapper();
|
||||||
|
|
||||||
|
annotationWrapper.recommendation = redactionLogEntry ? redactionLogEntry.recommendation : false;
|
||||||
|
if (annotationWrapper.recommendation) {
|
||||||
|
// if we have a manual redaction entry for a recommendation, hide the recommendation
|
||||||
|
if (manualRedactionEntry) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
annotationWrapper.recommendationType = redactionLogEntry.type.substr('recommendation_'.length);
|
||||||
|
}
|
||||||
annotationWrapper.comments = comments ? comments : [];
|
annotationWrapper.comments = comments ? comments : [];
|
||||||
annotationWrapper.userId = manualRedactionEntry?.user || idRemoval?.user;
|
annotationWrapper.userId = manualRedactionEntry?.user || idRemoval?.user;
|
||||||
|
|
||||||
|
|||||||
@ -63,16 +63,16 @@ export class FileDataModel {
|
|||||||
pairs.push({
|
pairs.push({
|
||||||
redactionLogEntry: rdl,
|
redactionLogEntry: rdl,
|
||||||
// only not declined
|
// only not declined
|
||||||
manualRedactionEntry: this.manualRedactions.entriesToAdd.find((eta) => eta.id === rdl.id),
|
manualRedactionEntry: this.manualRedactions.entriesToAdd.find((eta) => (eta.id === rdl.id || eta.reason === rdl.id) && this._dateValid(eta)),
|
||||||
// only not declined
|
// only not declined
|
||||||
idRemoval: this.manualRedactions.idsToRemove.find((idr) => idr.id === rdl.id),
|
idRemoval: this.manualRedactions.idsToRemove.find((idr) => idr.id === rdl.id && this._dateValid(idr)),
|
||||||
comments: this.manualRedactions.comments[rdl.id]
|
comments: this.manualRedactions.comments[rdl.id]
|
||||||
});
|
});
|
||||||
});
|
});
|
||||||
|
|
||||||
this.manualRedactions.entriesToAdd.forEach((eta) => {
|
this.manualRedactions.entriesToAdd.forEach((eta) => {
|
||||||
// only not declined
|
// only not declined
|
||||||
if (new Date(eta.processedDate).getTime() > new Date(this.fileStatus.lastProcessed).getTime() || !eta.processedDate) {
|
if (this._dateValid(eta)) {
|
||||||
const redactionLogEntry = this.redactionLog.redactionLogEntry.find((rdl) => rdl.id === eta.id);
|
const redactionLogEntry = this.redactionLog.redactionLogEntry.find((rdl) => rdl.id === eta.id);
|
||||||
if (!redactionLogEntry) {
|
if (!redactionLogEntry) {
|
||||||
pairs.push({
|
pairs.push({
|
||||||
@ -88,4 +88,8 @@ export class FileDataModel {
|
|||||||
|
|
||||||
return pairs;
|
return pairs;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
private _dateValid(entry: ManualRedactionEntry | IdRemoval): boolean {
|
||||||
|
return new Date(entry.processedDate).getTime() > new Date(this.fileStatus.lastProcessed).getTime() || !entry.processedDate;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@ -1,12 +1,13 @@
|
|||||||
import { Injectable } from '@angular/core';
|
import { Injectable } from '@angular/core';
|
||||||
import { AppStateService } from '../../../state/app-state.service';
|
import { AppStateService } from '../../../state/app-state.service';
|
||||||
import { DictionaryControllerService, ManualRedactionControllerService, ManualRedactionEntry } from '@redaction/red-ui-http';
|
import { AddRedactionRequest, DictionaryControllerService, ManualRedactionControllerService, ManualRedactionEntry } from '@redaction/red-ui-http';
|
||||||
import { AnnotationWrapper } from '../model/annotation.wrapper';
|
import { AnnotationWrapper } from '../model/annotation.wrapper';
|
||||||
import { NotificationService, NotificationType } from '../../../notification/notification.service';
|
import { NotificationService, NotificationType } from '../../../notification/notification.service';
|
||||||
import { TranslateService } from '@ngx-translate/core';
|
import { TranslateService } from '@ngx-translate/core';
|
||||||
import { tap } from 'rxjs/operators';
|
import { tap } from 'rxjs/operators';
|
||||||
import { UserService } from '../../../user/user.service';
|
import { UserService } from '../../../user/user.service';
|
||||||
import { PermissionsService } from '../../../common/service/permissions.service';
|
import { PermissionsService } from '../../../common/service/permissions.service';
|
||||||
|
import { of } from 'rxjs';
|
||||||
|
|
||||||
@Injectable({
|
@Injectable({
|
||||||
providedIn: 'root'
|
providedIn: 'root'
|
||||||
@ -43,6 +44,17 @@ export class ManualAnnotationService {
|
|||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
addRecommendation(annotation: AnnotationWrapper) {
|
||||||
|
const manualRedactionEntry: AddRedactionRequest = {};
|
||||||
|
manualRedactionEntry.addToDictionary = true;
|
||||||
|
manualRedactionEntry.reason = annotation.id; // set the ID as reason, so we can hide the suggestion
|
||||||
|
manualRedactionEntry.value = annotation.value;
|
||||||
|
manualRedactionEntry.positions = annotation.positions;
|
||||||
|
manualRedactionEntry.type = annotation.recommendationType;
|
||||||
|
manualRedactionEntry.comment = { text: 'Accepted Recommendation' };
|
||||||
|
return this.addAnnotation(manualRedactionEntry);
|
||||||
|
}
|
||||||
|
|
||||||
// this wraps
|
// this wraps
|
||||||
// /manualRedaction/redaction/add
|
// /manualRedaction/redaction/add
|
||||||
// /manualRedaction/request/add
|
// /manualRedaction/request/add
|
||||||
|
|||||||
@ -342,6 +342,9 @@
|
|||||||
"remove-from-dict": "Approve and remove from dictionary",
|
"remove-from-dict": "Approve and remove from dictionary",
|
||||||
"only-here": "Approve only here"
|
"only-here": "Approve only here"
|
||||||
},
|
},
|
||||||
|
"accept-recommendation": {
|
||||||
|
"label": "Accept Recommendation"
|
||||||
|
},
|
||||||
"suggest-remove-annotation": "Remove or Suggest to remove this entry",
|
"suggest-remove-annotation": "Remove or Suggest to remove this entry",
|
||||||
"reject-suggestion": "Reject Suggestion",
|
"reject-suggestion": "Reject Suggestion",
|
||||||
"remove-annotation": {
|
"remove-annotation": {
|
||||||
|
|||||||
Loading…
x
Reference in New Issue
Block a user