Added handling for recommendations

This commit is contained in:
Timo 2020-12-15 11:14:51 +02:00
parent 5c06b6b6e5
commit dd53e652fe
9 changed files with 107 additions and 15 deletions

View File

@ -1,8 +1,19 @@
<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
(action)="acceptSuggestion($event, annotation)"
type="dark-bg"
*ngIf="canAcceptSuggestion"
tooltipPosition="before"
tooltip="annotation-actions.accept-suggestion.label"
icon="red:check-alt"
>
@ -13,6 +24,7 @@
*ngIf="canUndoAnnotation"
type="dark-bg"
icon="red:undo"
tooltipPosition="before"
tooltip="annotation-actions.undo"
>
</redaction-circle-button>
@ -22,6 +34,7 @@
type="dark-bg"
icon="red:close"
*ngIf="canRejectSuggestion"
tooltipPosition="before"
tooltip="annotation-actions.reject-suggestion"
>
</redaction-circle-button>
@ -31,6 +44,7 @@
type="dark-bg"
icon="red:trash"
*ngIf="canDirectlySuggestToRemoveAnnotation"
tooltipPosition="before"
tooltip="annotation-actions.suggest-remove-annotation"
>
</redaction-circle-button>
@ -40,6 +54,7 @@
(action)="openMenu($event)"
[class.active]="menuOpen"
[matMenuTriggerFor]="menu"
tooltipPosition="before"
tooltip="annotation-actions.suggest-remove-annotation"
type="dark-bg"
icon="red:trash"

View File

@ -13,6 +13,10 @@
padding-top: 8px;
background: linear-gradient(to right, rgba(255, 255, 255, 0) 0%, #f9fafb, #f9fafb, #f9fafb);
redaction-circle-button {
display: block;
}
.confirm.active {
background-color: $grey-2;
}

View File

@ -16,7 +16,7 @@ export class AnnotationActionsComponent implements OnInit {
@Input() annotation: AnnotationWrapper;
@Input() canPerformAnnotationActions: boolean;
@Output() annotationsChanged = new EventEmitter();
@Output() annotationsChanged = new EventEmitter<boolean>();
suggestionType: TypeValue;
menuOpen: boolean;
@ -46,11 +46,18 @@ export class AnnotationActionsComponent implements OnInit {
}
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() {
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() {
@ -84,8 +91,8 @@ export class AnnotationActionsComponent implements OnInit {
private _processObsAndEmit(obs: Observable<any>) {
obs.subscribe(
() => {
this.annotationsChanged.emit();
(data) => {
this.annotationsChanged.emit(!!data?.annotationId);
},
() => {
this.annotationsChanged.emit();
@ -109,4 +116,9 @@ export class AnnotationActionsComponent implements OnInit {
get dictionaryColor() {
return this.appStateService.getDictionaryColor('suggestion-add-dictionary');
}
convertRecommendationToAnnotation($event: any, annotation: AnnotationWrapper) {
$event.stopPropagation();
this._processObsAndEmit(this._manualAnnotationService.addRecommendation(annotation));
}
}

View File

@ -200,7 +200,7 @@
</div>
<redaction-comments [annotation]="annotation"></redaction-comments>
<redaction-annotation-actions
(annotationsChanged)="annotationsChangedByReviewAction(annotation)"
(annotationsChanged)="annotationsChangedByReviewAction(true, annotation)"
[annotation]="annotation"
[canPerformAnnotationActions]="canPerformAnnotationActions"
></redaction-annotation-actions>

View File

@ -441,13 +441,41 @@ export class FilePreviewScreenComponent implements OnInit, OnDestroy {
});
}
async annotationsChangedByReviewAction(annotation: AnnotationWrapper) {
const viewerAnnotation = this.activeViewer.annotManager.getAnnotationById(annotation.id);
private async _cleanupAndRedrawManualAnnotationsForEntirePage(page: number) {
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) {
this.activeViewer.annotManager.deleteAnnotation(viewerAnnotation, true, true);
}
this.fileData.fileStatus = await this.appStateService.reloadActiveFile();
this._cleanupAndRedrawManualAnnotations(annotation.id);
}
async fileActionPerformed(action: string) {
@ -496,5 +524,6 @@ export class FilePreviewScreenComponent implements OnInit, OnDestroy {
async openHTMLDebug() {
window.open(`/html-debug/${this.fileId}`, '_blank');
}
// <!-- End Dev Mode Features-->
}

View File

@ -1,7 +1,6 @@
import { Comment, IdRemoval, ManualRedactionEntry, Point, Rectangle, RedactionLogEntry, TypeValue } from '@redaction/red-ui-http';
import { UserWrapper } from '../../../user/user.service';
import { FileStatusWrapper } from './file-status.wrapper';
import { humanize } from '../../../utils/functions';
export class AnnotationWrapper {
superType:
@ -31,7 +30,9 @@ export class AnnotationWrapper {
redaction: boolean;
status: string;
dictionaryOperation: boolean;
recommendation: boolean;
positions: Rectangle[];
recommendationType: string;
get isIgnored() {
return this.superType === 'ignore';
@ -81,6 +82,10 @@ export class AnnotationWrapper {
return this.dictionaryOperation;
}
get isRecommendation() {
return this.recommendation;
}
static fromData(
user: UserWrapper,
dictionaryData: { [p: string]: TypeValue },
@ -107,6 +112,14 @@ export class 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.userId = manualRedactionEntry?.user || idRemoval?.user;

View File

@ -63,16 +63,16 @@ export class FileDataModel {
pairs.push({
redactionLogEntry: rdl,
// 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
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]
});
});
this.manualRedactions.entriesToAdd.forEach((eta) => {
// 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);
if (!redactionLogEntry) {
pairs.push({
@ -88,4 +88,8 @@ export class FileDataModel {
return pairs;
}
private _dateValid(entry: ManualRedactionEntry | IdRemoval): boolean {
return new Date(entry.processedDate).getTime() > new Date(this.fileStatus.lastProcessed).getTime() || !entry.processedDate;
}
}

View File

@ -1,12 +1,13 @@
import { Injectable } from '@angular/core';
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 { NotificationService, NotificationType } from '../../../notification/notification.service';
import { TranslateService } from '@ngx-translate/core';
import { tap } from 'rxjs/operators';
import { UserService } from '../../../user/user.service';
import { PermissionsService } from '../../../common/service/permissions.service';
import { of } from 'rxjs';
@Injectable({
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
// /manualRedaction/redaction/add
// /manualRedaction/request/add

View File

@ -342,6 +342,9 @@
"remove-from-dict": "Approve and remove from dictionary",
"only-here": "Approve only here"
},
"accept-recommendation": {
"label": "Accept Recommendation"
},
"suggest-remove-annotation": "Remove or Suggest to remove this entry",
"reject-suggestion": "Reject Suggestion",
"remove-annotation": {