further fixes for state handling of annotations
This commit is contained in:
parent
57a9f86fd0
commit
92d3b866ce
@ -7,6 +7,7 @@ import { Observable } from 'rxjs';
|
|||||||
import { TranslateService } from '@ngx-translate/core';
|
import { TranslateService } from '@ngx-translate/core';
|
||||||
import { AddRedactionRequest } from '@redaction/red-ui-http';
|
import { AddRedactionRequest } from '@redaction/red-ui-http';
|
||||||
import { getFirstRelevantTextPart } from '../../utils/functions';
|
import { getFirstRelevantTextPart } from '../../utils/functions';
|
||||||
|
import { AnnotationPermissions } from '../../screens/file/model/annotation.permissions';
|
||||||
|
|
||||||
@Injectable({
|
@Injectable({
|
||||||
providedIn: 'root'
|
providedIn: 'root'
|
||||||
@ -20,39 +21,6 @@ export class AnnotationActionsService {
|
|||||||
private readonly _dialogService: DialogService
|
private readonly _dialogService: DialogService
|
||||||
) {}
|
) {}
|
||||||
|
|
||||||
public canAcceptSuggestion(annotation: AnnotationWrapper): boolean {
|
|
||||||
return this._permissionsService.isManagerAndOwner() && (annotation.isSuggestion || annotation.isDeclinedSuggestion);
|
|
||||||
}
|
|
||||||
|
|
||||||
public canRejectSuggestion(annotation: AnnotationWrapper): boolean {
|
|
||||||
// i can reject whatever i may not undo
|
|
||||||
return !this.canUndoAnnotation(annotation) && this.canAcceptSuggestion(annotation) && !annotation.isDeclinedSuggestion;
|
|
||||||
}
|
|
||||||
|
|
||||||
public canDirectlySuggestToRemoveAnnotation(annotation: AnnotationWrapper): boolean {
|
|
||||||
return (
|
|
||||||
// annotation.isHint || // HINTS CAN NO LONGER BE REMOVED DIRECTLY ONLY VIA DICTIONARY ACTION
|
|
||||||
annotation.isManualRedaction && this._permissionsService.isManagerAndOwner() && !this.canUndoAnnotation(annotation) && !annotation.isRecommendation
|
|
||||||
);
|
|
||||||
}
|
|
||||||
|
|
||||||
public requiresSuggestionRemoveMenu(annotation: AnnotationWrapper): boolean {
|
|
||||||
return (annotation.isRedacted || annotation.isIgnored) && !annotation.isRecommendation;
|
|
||||||
}
|
|
||||||
|
|
||||||
public canConvertRecommendationToAnnotation(annotation: AnnotationWrapper): boolean {
|
|
||||||
// recommendations that have not already been turned into a suggestion
|
|
||||||
return annotation.isRecommendation && !annotation.isConvertedRecommendation;
|
|
||||||
}
|
|
||||||
|
|
||||||
public canUndoAnnotation(annotation: AnnotationWrapper): boolean {
|
|
||||||
// suggestions of current user can be undone
|
|
||||||
const isSuggestionOfCurrentUser = annotation.isSuggestion && annotation.userId === this._permissionsService.currentUserId;
|
|
||||||
// or any performed manual actions and you are the manager, provided that it is not a suggestion
|
|
||||||
const isActionOfManger = this._permissionsService.isManagerAndOwner() && annotation.userId === this._permissionsService.currentUserId;
|
|
||||||
return isSuggestionOfCurrentUser || isActionOfManger;
|
|
||||||
}
|
|
||||||
|
|
||||||
public acceptSuggestion($event: MouseEvent, annotation: AnnotationWrapper, annotationsChanged: EventEmitter<AnnotationWrapper>) {
|
public acceptSuggestion($event: MouseEvent, annotation: AnnotationWrapper, annotationsChanged: EventEmitter<AnnotationWrapper>) {
|
||||||
$event?.stopPropagation();
|
$event?.stopPropagation();
|
||||||
console.log(annotation.isModifyDictionary);
|
console.log(annotation.isModifyDictionary);
|
||||||
@ -117,7 +85,9 @@ export class AnnotationActionsService {
|
|||||||
public getViewerAvailableActions(annotation: AnnotationWrapper, annotationsChanged: EventEmitter<AnnotationWrapper>): {}[] {
|
public getViewerAvailableActions(annotation: AnnotationWrapper, annotationsChanged: EventEmitter<AnnotationWrapper>): {}[] {
|
||||||
const availableActions = [];
|
const availableActions = [];
|
||||||
|
|
||||||
if (this.canConvertRecommendationToAnnotation(annotation)) {
|
const annotationPermissions = AnnotationPermissions.forUser(this._permissionsService.currentUser, annotation);
|
||||||
|
|
||||||
|
if (annotationPermissions.canAcceptRecommendation) {
|
||||||
availableActions.push({
|
availableActions.push({
|
||||||
type: 'actionButton',
|
type: 'actionButton',
|
||||||
img: '/assets/icons/general/check-alt.svg',
|
img: '/assets/icons/general/check-alt.svg',
|
||||||
@ -130,7 +100,7 @@ export class AnnotationActionsService {
|
|||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
if (this.canAcceptSuggestion(annotation)) {
|
if (annotationPermissions.canAcceptSuggestion) {
|
||||||
availableActions.push({
|
availableActions.push({
|
||||||
type: 'actionButton',
|
type: 'actionButton',
|
||||||
img: '/assets/icons/general/check-alt.svg',
|
img: '/assets/icons/general/check-alt.svg',
|
||||||
@ -143,7 +113,7 @@ export class AnnotationActionsService {
|
|||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
if (this.canUndoAnnotation(annotation)) {
|
if (annotationPermissions.canUndo) {
|
||||||
availableActions.push({
|
availableActions.push({
|
||||||
type: 'actionButton',
|
type: 'actionButton',
|
||||||
img: '/assets/icons/general/undo.svg',
|
img: '/assets/icons/general/undo.svg',
|
||||||
@ -156,7 +126,7 @@ export class AnnotationActionsService {
|
|||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
if (this.canRejectSuggestion(annotation)) {
|
if (annotationPermissions.canRejectSuggestion) {
|
||||||
availableActions.push({
|
availableActions.push({
|
||||||
type: 'actionButton',
|
type: 'actionButton',
|
||||||
img: '/assets/icons/general/close.svg',
|
img: '/assets/icons/general/close.svg',
|
||||||
@ -169,33 +139,20 @@ export class AnnotationActionsService {
|
|||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
if (this.canDirectlySuggestToRemoveAnnotation(annotation)) {
|
if (annotationPermissions.canRemoveOrSuggestToRemoveOnlyHere) {
|
||||||
availableActions.push({
|
availableActions.push({
|
||||||
type: 'actionButton',
|
type: 'actionButton',
|
||||||
img: '/assets/icons/general/trash.svg',
|
img: '/assets/icons/general/close.svg',
|
||||||
title: this._translateService.instant('annotation-actions.suggest-remove-annotation'),
|
title: this._translateService.instant('annotation-actions.suggest-remove-annotation'),
|
||||||
onClick: () => {
|
onClick: () => {
|
||||||
this._ngZone.run(() => {
|
this._ngZone.run(() => {
|
||||||
this.suggestRemoveAnnotation(null, annotation, true, annotationsChanged);
|
this.suggestRemoveAnnotation(null, annotation, false, annotationsChanged);
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
if (this.requiresSuggestionRemoveMenu(annotation)) {
|
if (annotationPermissions.canRemoveOrSuggestToRemoveFromDictionary) {
|
||||||
if (!annotation.isIgnored) {
|
|
||||||
availableActions.push({
|
|
||||||
type: 'actionButton',
|
|
||||||
img: '/assets/icons/general/close.svg',
|
|
||||||
title: this._translateService.instant('annotation-actions.remove-annotation.only-here'),
|
|
||||||
onClick: () => {
|
|
||||||
this._ngZone.run(() => {
|
|
||||||
this.suggestRemoveAnnotation(null, annotation, false, annotationsChanged);
|
|
||||||
});
|
|
||||||
}
|
|
||||||
});
|
|
||||||
}
|
|
||||||
|
|
||||||
availableActions.push({
|
availableActions.push({
|
||||||
type: 'actionButton',
|
type: 'actionButton',
|
||||||
img: '/assets/icons/general/trash.svg',
|
img: '/assets/icons/general/trash.svg',
|
||||||
@ -206,19 +163,19 @@ export class AnnotationActionsService {
|
|||||||
});
|
});
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
|
}
|
||||||
|
|
||||||
if (annotation.canBeMarkedAsFalsePositive) {
|
if (annotationPermissions.canMarkAsFalsePositive) {
|
||||||
availableActions.push({
|
availableActions.push({
|
||||||
type: 'actionButton',
|
type: 'actionButton',
|
||||||
img: '/assets/icons/general/thumb-down.svg',
|
img: '/assets/icons/general/thumb-down.svg',
|
||||||
title: this._translateService.instant('annotation-actions.remove-annotation.false-positive'),
|
title: this._translateService.instant('annotation-actions.remove-annotation.false-positive'),
|
||||||
onClick: () => {
|
onClick: () => {
|
||||||
this._ngZone.run(() => {
|
this._ngZone.run(() => {
|
||||||
this.markAsFalsePositive(null, annotation, annotationsChanged);
|
this.markAsFalsePositive(null, annotation, annotationsChanged);
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
return availableActions;
|
return availableActions;
|
||||||
|
|||||||
@ -2,7 +2,7 @@
|
|||||||
<redaction-circle-button
|
<redaction-circle-button
|
||||||
(action)="annotationActionsService.convertRecommendationToAnnotation($event, annotation, annotationsChanged)"
|
(action)="annotationActionsService.convertRecommendationToAnnotation($event, annotation, annotationsChanged)"
|
||||||
type="dark-bg"
|
type="dark-bg"
|
||||||
*ngIf="annotationActionsService.canConvertRecommendationToAnnotation(annotation)"
|
*ngIf="annotationPermissions.canAcceptRecommendation"
|
||||||
tooltipPosition="before"
|
tooltipPosition="before"
|
||||||
tooltip="annotation-actions.accept-recommendation.label"
|
tooltip="annotation-actions.accept-recommendation.label"
|
||||||
icon="red:check-alt"
|
icon="red:check-alt"
|
||||||
@ -12,7 +12,7 @@
|
|||||||
<redaction-circle-button
|
<redaction-circle-button
|
||||||
(action)="annotationActionsService.acceptSuggestion($event, annotation, annotationsChanged)"
|
(action)="annotationActionsService.acceptSuggestion($event, annotation, annotationsChanged)"
|
||||||
type="dark-bg"
|
type="dark-bg"
|
||||||
*ngIf="annotationActionsService.canAcceptSuggestion(annotation)"
|
*ngIf="annotationPermissions.canAcceptSuggestion"
|
||||||
tooltipPosition="before"
|
tooltipPosition="before"
|
||||||
tooltip="annotation-actions.accept-suggestion.label"
|
tooltip="annotation-actions.accept-suggestion.label"
|
||||||
icon="red:check-alt"
|
icon="red:check-alt"
|
||||||
@ -21,7 +21,7 @@
|
|||||||
|
|
||||||
<redaction-circle-button
|
<redaction-circle-button
|
||||||
(action)="annotationActionsService.undoDirectAction($event, annotation, annotationsChanged)"
|
(action)="annotationActionsService.undoDirectAction($event, annotation, annotationsChanged)"
|
||||||
*ngIf="annotationActionsService.canUndoAnnotation(annotation)"
|
*ngIf="annotationPermissions.canUndo"
|
||||||
type="dark-bg"
|
type="dark-bg"
|
||||||
icon="red:undo"
|
icon="red:undo"
|
||||||
tooltipPosition="before"
|
tooltipPosition="before"
|
||||||
@ -33,24 +33,24 @@
|
|||||||
(action)="annotationActionsService.rejectSuggestion($event, annotation, annotationsChanged)"
|
(action)="annotationActionsService.rejectSuggestion($event, annotation, annotationsChanged)"
|
||||||
type="dark-bg"
|
type="dark-bg"
|
||||||
icon="red:close"
|
icon="red:close"
|
||||||
*ngIf="annotationActionsService.canRejectSuggestion(annotation)"
|
*ngIf="annotationPermissions.canRejectSuggestion"
|
||||||
tooltipPosition="before"
|
tooltipPosition="before"
|
||||||
tooltip="annotation-actions.reject-suggestion"
|
tooltip="annotation-actions.reject-suggestion"
|
||||||
>
|
>
|
||||||
</redaction-circle-button>
|
</redaction-circle-button>
|
||||||
|
|
||||||
<redaction-circle-button
|
<redaction-circle-button
|
||||||
(action)="annotationActionsService.suggestRemoveAnnotation($event, annotation, true, annotationsChanged)"
|
(action)="annotationActionsService.suggestRemoveAnnotation($event, annotation, false, annotationsChanged)"
|
||||||
type="dark-bg"
|
type="dark-bg"
|
||||||
icon="red:trash"
|
icon="red:trash"
|
||||||
*ngIf="annotationActionsService.canDirectlySuggestToRemoveAnnotation(annotation)"
|
*ngIf="annotationPermissions.canRemoveOrSuggestToRemoveOnlyHere && !annotationPermissions.canPerformMultipleRemoveActions"
|
||||||
tooltipPosition="before"
|
tooltipPosition="before"
|
||||||
tooltip="annotation-actions.suggest-remove-annotation"
|
tooltip="annotation-actions.suggest-remove-annotation"
|
||||||
>
|
>
|
||||||
</redaction-circle-button>
|
</redaction-circle-button>
|
||||||
|
|
||||||
<redaction-circle-button
|
<redaction-circle-button
|
||||||
*ngIf="annotationActionsService.requiresSuggestionRemoveMenu(annotation)"
|
*ngIf="annotationPermissions.canPerformMultipleRemoveActions"
|
||||||
(action)="openMenu($event)"
|
(action)="openMenu($event)"
|
||||||
[class.active]="menuOpen"
|
[class.active]="menuOpen"
|
||||||
[matMenuTriggerFor]="menu"
|
[matMenuTriggerFor]="menu"
|
||||||
@ -62,14 +62,18 @@
|
|||||||
</redaction-circle-button>
|
</redaction-circle-button>
|
||||||
|
|
||||||
<mat-menu #menu="matMenu" (closed)="onMenuClosed()" xPosition="before">
|
<mat-menu #menu="matMenu" (closed)="onMenuClosed()" xPosition="before">
|
||||||
<div (click)="annotationActionsService.suggestRemoveAnnotation($event, annotation, true, annotationsChanged)" mat-menu-item>
|
<div
|
||||||
|
(click)="annotationActionsService.suggestRemoveAnnotation($event, annotation, true, annotationsChanged)"
|
||||||
|
mat-menu-item
|
||||||
|
*ngIf="annotationPermissions.canRemoveOrSuggestToRemoveFromDictionary"
|
||||||
|
>
|
||||||
<redaction-annotation-icon [type]="'rhombus'" [label]="'S'" [color]="dictionaryColor"></redaction-annotation-icon>
|
<redaction-annotation-icon [type]="'rhombus'" [label]="'S'" [color]="dictionaryColor"></redaction-annotation-icon>
|
||||||
<div [translate]="'annotation-actions.remove-annotation.remove-from-dict'"></div>
|
<div [translate]="'annotation-actions.remove-annotation.remove-from-dict'"></div>
|
||||||
</div>
|
</div>
|
||||||
<div
|
<div
|
||||||
(click)="annotationActionsService.suggestRemoveAnnotation($event, annotation, false, annotationsChanged)"
|
(click)="annotationActionsService.suggestRemoveAnnotation($event, annotation, false, annotationsChanged)"
|
||||||
mat-menu-item
|
mat-menu-item
|
||||||
*ngIf="!annotation.isIgnored"
|
*ngIf="annotationPermissions.canRemoveOrSuggestToRemoveOnlyHere"
|
||||||
>
|
>
|
||||||
<redaction-annotation-icon [type]="'rhombus'" [label]="'S'" [color]="suggestionColor"></redaction-annotation-icon>
|
<redaction-annotation-icon [type]="'rhombus'" [label]="'S'" [color]="suggestionColor"></redaction-annotation-icon>
|
||||||
<div translate="annotation-actions.remove-annotation.only-here"></div>
|
<div translate="annotation-actions.remove-annotation.only-here"></div>
|
||||||
@ -78,7 +82,7 @@
|
|||||||
<div
|
<div
|
||||||
(click)="annotationActionsService.markAsFalsePositive($event, annotation, annotationsChanged)"
|
(click)="annotationActionsService.markAsFalsePositive($event, annotation, annotationsChanged)"
|
||||||
mat-menu-item
|
mat-menu-item
|
||||||
*ngIf="annotation.canBeMarkedAsFalsePositive"
|
*ngIf="annotationPermissions.canMarkAsFalsePositive"
|
||||||
>
|
>
|
||||||
<mat-icon svgIcon="red:thumb-down" class="false-positive-icon"></mat-icon>
|
<mat-icon svgIcon="red:thumb-down" class="false-positive-icon"></mat-icon>
|
||||||
<div translate="annotation-actions.remove-annotation.false-positive"></div>
|
<div translate="annotation-actions.remove-annotation.false-positive"></div>
|
||||||
|
|||||||
@ -1,7 +1,8 @@
|
|||||||
import { Component, EventEmitter, Input, OnInit, Output } from '@angular/core';
|
import { Component, EventEmitter, Input, OnInit, Output } from '@angular/core';
|
||||||
import { AnnotationWrapper } from '../model/annotation.wrapper';
|
import { AnnotationWrapper } from '../model/annotation.wrapper';
|
||||||
import { AppStateService } from '../../../state/app-state.service';
|
import { AppStateService } from '../../../state/app-state.service';
|
||||||
import { TypeValue } from '@redaction/red-ui-http';
|
import { PermissionsService } from '../../../common/service/permissions.service';
|
||||||
|
import { AnnotationPermissions } from '../model/annotation.permissions';
|
||||||
import { AnnotationActionsService } from '../../../common/service/annotation-actions.service';
|
import { AnnotationActionsService } from '../../../common/service/annotation-actions.service';
|
||||||
|
|
||||||
@Component({
|
@Component({
|
||||||
@ -15,13 +16,17 @@ export class AnnotationActionsComponent implements OnInit {
|
|||||||
|
|
||||||
@Output() annotationsChanged = new EventEmitter<AnnotationWrapper>();
|
@Output() annotationsChanged = new EventEmitter<AnnotationWrapper>();
|
||||||
|
|
||||||
suggestionType: TypeValue;
|
|
||||||
menuOpen: boolean;
|
menuOpen: boolean;
|
||||||
|
annotationPermissions: AnnotationPermissions;
|
||||||
|
|
||||||
constructor(public appStateService: AppStateService, public annotationActionsService: AnnotationActionsService) {}
|
constructor(
|
||||||
|
public appStateService: AppStateService,
|
||||||
|
private _permissionsService: PermissionsService,
|
||||||
|
public annotationActionsService: AnnotationActionsService
|
||||||
|
) {}
|
||||||
|
|
||||||
ngOnInit(): void {
|
ngOnInit(): void {
|
||||||
this.suggestionType = this.appStateService.getDictionaryTypeValue('suggestion');
|
this.annotationPermissions = AnnotationPermissions.forUser(this._permissionsService.currentUser, this.annotation);
|
||||||
}
|
}
|
||||||
|
|
||||||
public openMenu($event: MouseEvent) {
|
public openMenu($event: MouseEvent) {
|
||||||
|
|||||||
@ -0,0 +1,36 @@
|
|||||||
|
import { UserWrapper } from '../../../user/user.service';
|
||||||
|
import { AnnotationWrapper } from './annotation.wrapper';
|
||||||
|
|
||||||
|
export class AnnotationPermissions {
|
||||||
|
canUndo: boolean;
|
||||||
|
|
||||||
|
canAcceptRecommendation: boolean;
|
||||||
|
canMarkAsFalsePositive: boolean;
|
||||||
|
|
||||||
|
canRemoveOrSuggestToRemoveOnlyHere: boolean;
|
||||||
|
canRemoveOrSuggestToRemoveFromDictionary: boolean;
|
||||||
|
|
||||||
|
canAcceptSuggestion: boolean;
|
||||||
|
canRejectSuggestion: boolean;
|
||||||
|
|
||||||
|
public static forUser(user: UserWrapper, annotation: AnnotationWrapper) {
|
||||||
|
const permissions: AnnotationPermissions = new AnnotationPermissions();
|
||||||
|
|
||||||
|
permissions.canUndo = annotation.userId === user.id && annotation.isUndoableSuperType;
|
||||||
|
|
||||||
|
permissions.canAcceptRecommendation = annotation.isRecommendation;
|
||||||
|
permissions.canMarkAsFalsePositive = annotation.canBeMarkedAsFalsePositive;
|
||||||
|
|
||||||
|
permissions.canRemoveOrSuggestToRemoveOnlyHere = annotation.isRedacted;
|
||||||
|
permissions.canRemoveOrSuggestToRemoveFromDictionary = annotation.isRedacted && !annotation.isManualRedaction;
|
||||||
|
|
||||||
|
permissions.canAcceptSuggestion = user.isManager && (annotation.isSuggestion || annotation.isDeclinedSuggestion);
|
||||||
|
permissions.canRejectSuggestion = user.isManager && (annotation.isSuggestion || (annotation.isReadyForAnalysis && !permissions.canUndo));
|
||||||
|
|
||||||
|
return permissions;
|
||||||
|
}
|
||||||
|
|
||||||
|
public get canPerformMultipleRemoveActions() {
|
||||||
|
return <any>this.canMarkAsFalsePositive + <any>this.canRemoveOrSuggestToRemoveFromDictionary + <any>this.canRemoveOrSuggestToRemoveOnlyHere >= 2;
|
||||||
|
}
|
||||||
|
}
|
||||||
@ -5,6 +5,7 @@ export class AnnotationWrapper {
|
|||||||
superType:
|
superType:
|
||||||
| 'add-dictionary'
|
| 'add-dictionary'
|
||||||
| 'remove-dictionary'
|
| 'remove-dictionary'
|
||||||
|
| 'remove-only-here'
|
||||||
| 'suggestion-add-dictionary'
|
| 'suggestion-add-dictionary'
|
||||||
| 'suggestion-remove-dictionary'
|
| 'suggestion-remove-dictionary'
|
||||||
| 'suggestion-add'
|
| 'suggestion-add'
|
||||||
@ -12,8 +13,10 @@ export class AnnotationWrapper {
|
|||||||
| 'ignore'
|
| 'ignore'
|
||||||
| 'redaction'
|
| 'redaction'
|
||||||
| 'manual-redaction'
|
| 'manual-redaction'
|
||||||
|
| 'recommendation'
|
||||||
| 'hint'
|
| 'hint'
|
||||||
| 'declined-suggestion';
|
| 'declined-suggestion';
|
||||||
|
|
||||||
dictionary: string;
|
dictionary: string;
|
||||||
color: string;
|
color: string;
|
||||||
comments: Comment[] = [];
|
comments: Comment[] = [];
|
||||||
@ -29,18 +32,26 @@ export class AnnotationWrapper {
|
|||||||
status: string;
|
status: string;
|
||||||
dictionaryOperation: boolean;
|
dictionaryOperation: boolean;
|
||||||
positions: Rectangle[];
|
positions: Rectangle[];
|
||||||
|
|
||||||
recommendation: boolean;
|
|
||||||
recommendationType: string;
|
recommendationType: string;
|
||||||
pendingRecommendationAnnotationId: string;
|
|
||||||
|
|
||||||
textAfter?: string;
|
textAfter?: string;
|
||||||
textBefore?: string;
|
textBefore?: string;
|
||||||
|
|
||||||
constructor() {}
|
constructor() {}
|
||||||
|
|
||||||
|
get isUndoableSuperType() {
|
||||||
|
return (
|
||||||
|
this.superType === 'add-dictionary' ||
|
||||||
|
this.superType === 'remove-dictionary' ||
|
||||||
|
this.superType === 'suggestion-add-dictionary' ||
|
||||||
|
this.superType === 'suggestion-remove-dictionary' ||
|
||||||
|
this.superType === 'suggestion-add' ||
|
||||||
|
this.superType === 'suggestion-remove'
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
get isDictionaryBased() {
|
get isDictionaryBased() {
|
||||||
return (this.isHint || this.isRedacted) && !this.isManualRedaction;
|
return this.isHint || this.superType === 'redaction';
|
||||||
}
|
}
|
||||||
|
|
||||||
get descriptor() {
|
get descriptor() {
|
||||||
@ -58,7 +69,7 @@ export class AnnotationWrapper {
|
|||||||
}
|
}
|
||||||
|
|
||||||
get canBeMarkedAsFalsePositive() {
|
get canBeMarkedAsFalsePositive() {
|
||||||
return this.isRedacted && (this.hasTextAfter || this.hasTextBefore);
|
return this.superType === 'redaction' && (this.hasTextAfter || this.hasTextBefore);
|
||||||
}
|
}
|
||||||
|
|
||||||
get isSuperTypeBasedColor() {
|
get isSuperTypeBasedColor() {
|
||||||
@ -86,7 +97,7 @@ export class AnnotationWrapper {
|
|||||||
}
|
}
|
||||||
|
|
||||||
get isReadyForAnalysis() {
|
get isReadyForAnalysis() {
|
||||||
return this.superType === 'add-dictionary' || this.superType === 'remove-dictionary';
|
return this.superType === 'add-dictionary' || this.superType === 'remove-dictionary' || this.superType === 'remove-only-here';
|
||||||
}
|
}
|
||||||
|
|
||||||
get isApproved() {
|
get isApproved() {
|
||||||
@ -98,7 +109,7 @@ export class AnnotationWrapper {
|
|||||||
}
|
}
|
||||||
|
|
||||||
get isRedacted() {
|
get isRedacted() {
|
||||||
return this.superType === 'redaction';
|
return this.superType === 'redaction' || this.superType === 'manual-redaction';
|
||||||
}
|
}
|
||||||
|
|
||||||
get isSuggestion() {
|
get isSuggestion() {
|
||||||
@ -122,11 +133,11 @@ export class AnnotationWrapper {
|
|||||||
}
|
}
|
||||||
|
|
||||||
get isRecommendation() {
|
get isRecommendation() {
|
||||||
return this.recommendation;
|
return this.superType === 'recommendation';
|
||||||
}
|
}
|
||||||
|
|
||||||
get id() {
|
get id() {
|
||||||
return this.isConvertedRecommendation ? this.pendingRecommendationAnnotationId : this.annotationId;
|
return this.annotationId;
|
||||||
}
|
}
|
||||||
|
|
||||||
get x() {
|
get x() {
|
||||||
@ -153,61 +164,68 @@ export class AnnotationWrapper {
|
|||||||
annotationWrapper.textAfter = redactionLogEntry.textAfter;
|
annotationWrapper.textAfter = redactionLogEntry.textAfter;
|
||||||
annotationWrapper.dictionaryOperation = redactionLogEntry.dictionaryEntry;
|
annotationWrapper.dictionaryOperation = redactionLogEntry.dictionaryEntry;
|
||||||
annotationWrapper.userId = redactionLogEntry.userId;
|
annotationWrapper.userId = redactionLogEntry.userId;
|
||||||
annotationWrapper.content = AnnotationWrapper.createContent(redactionLogEntry);
|
annotationWrapper.content = AnnotationWrapper._createContent(redactionLogEntry);
|
||||||
AnnotationWrapper._setSuperType(annotationWrapper, redactionLogEntry);
|
AnnotationWrapper._setSuperType(annotationWrapper, redactionLogEntry);
|
||||||
AnnotationWrapper._handleRecommendations(annotationWrapper, redactionLogEntry);
|
AnnotationWrapper._handleRecommendations(annotationWrapper, redactionLogEntry);
|
||||||
|
annotationWrapper.typeLabel = 'annotation-type.' + annotationWrapper.superType;
|
||||||
annotationWrapper.content = annotationWrapper.id;
|
|
||||||
|
|
||||||
if (annotationWrapper.dictionary === 'PII') {
|
|
||||||
annotationWrapper.content = annotationWrapper.id;
|
|
||||||
console.log(annotationWrapper, redactionLogEntry, annotationWrapper.id);
|
|
||||||
}
|
|
||||||
|
|
||||||
return annotationWrapper;
|
return annotationWrapper;
|
||||||
}
|
}
|
||||||
|
|
||||||
private static _handleRecommendations(annotationWrapper: AnnotationWrapper, redactionLogEntry: RedactionLogEntryWrapper) {
|
private static _handleRecommendations(annotationWrapper: AnnotationWrapper, redactionLogEntry: RedactionLogEntryWrapper) {
|
||||||
annotationWrapper.recommendation = !!redactionLogEntry?.recommendation;
|
if (annotationWrapper.superType === 'recommendation') {
|
||||||
if (annotationWrapper.recommendation) {
|
|
||||||
annotationWrapper.recommendationType = redactionLogEntry.type.substr('recommendation_'.length);
|
annotationWrapper.recommendationType = redactionLogEntry.type.substr('recommendation_'.length);
|
||||||
if (annotationWrapper.isConvertedRecommendation) {
|
|
||||||
annotationWrapper.dictionary = annotationWrapper.recommendationType;
|
|
||||||
annotationWrapper.pendingRecommendationAnnotationId = redactionLogEntry.pendingRecommendationAnnotationId;
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
private static _setSuperType(annotationWrapper: AnnotationWrapper, redactionLogEntryWrapper: RedactionLogEntryWrapper) {
|
private static _setSuperType(annotationWrapper: AnnotationWrapper, redactionLogEntryWrapper: RedactionLogEntryWrapper) {
|
||||||
if (redactionLogEntryWrapper.manual && redactionLogEntryWrapper.status === 'DECLINED') {
|
if (redactionLogEntryWrapper.recommendation) {
|
||||||
|
annotationWrapper.superType = 'recommendation';
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (redactionLogEntryWrapper.status === 'DECLINED') {
|
||||||
annotationWrapper.superType = 'declined-suggestion';
|
annotationWrapper.superType = 'declined-suggestion';
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
if (redactionLogEntryWrapper.type === 'manual') {
|
if (annotationWrapper.dictionary === 'false_positive') {
|
||||||
annotationWrapper.superType = redactionLogEntryWrapper.status === 'REQUESTED' ? 'suggestion-add' : 'manual-redaction';
|
|
||||||
} else {
|
|
||||||
if (redactionLogEntryWrapper.status === 'REQUESTED') {
|
if (redactionLogEntryWrapper.status === 'REQUESTED') {
|
||||||
if (redactionLogEntryWrapper.dictionaryEntry) {
|
annotationWrapper.superType = 'suggestion-add-dictionary';
|
||||||
annotationWrapper.superType =
|
|
||||||
redactionLogEntryWrapper.manualRedactionType === 'ADD' ? 'suggestion-add-dictionary' : 'suggestion-remove-dictionary';
|
|
||||||
} else {
|
|
||||||
annotationWrapper.superType = redactionLogEntryWrapper.manualRedactionType === 'ADD' ? 'suggestion-add' : 'suggestion-remove';
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
if (redactionLogEntryWrapper.status === 'APPROVED') {
|
if (redactionLogEntryWrapper.status === 'APPROVED') {
|
||||||
annotationWrapper.superType = redactionLogEntryWrapper.manualRedactionType === 'ADD' ? 'add-dictionary' : 'remove-dictionary';
|
annotationWrapper.superType = 'add-dictionary';
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
if (redactionLogEntryWrapper.redacted) {
|
||||||
|
if (redactionLogEntryWrapper.type === 'manual') {
|
||||||
|
annotationWrapper.superType = redactionLogEntryWrapper.status === 'REQUESTED' ? 'suggestion-add' : 'manual-redaction';
|
||||||
|
} else {
|
||||||
|
if (redactionLogEntryWrapper.status === 'REQUESTED') {
|
||||||
|
if (redactionLogEntryWrapper.dictionaryEntry) {
|
||||||
|
annotationWrapper.superType =
|
||||||
|
redactionLogEntryWrapper.manualRedactionType === 'ADD' ? 'suggestion-add-dictionary' : 'suggestion-remove-dictionary';
|
||||||
|
} else {
|
||||||
|
annotationWrapper.superType = redactionLogEntryWrapper.manualRedactionType === 'ADD' ? 'suggestion-add' : 'suggestion-remove';
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if (redactionLogEntryWrapper.status === 'APPROVED') {
|
||||||
|
if (redactionLogEntryWrapper.dictionaryEntry) {
|
||||||
|
annotationWrapper.superType = redactionLogEntryWrapper.manualRedactionType === 'ADD' ? 'add-dictionary' : 'remove-dictionary';
|
||||||
|
} else {
|
||||||
|
annotationWrapper.superType = redactionLogEntryWrapper.manualRedactionType === 'ADD' ? 'manual-redaction' : 'remove-only-here';
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
if (!annotationWrapper.superType) {
|
if (!annotationWrapper.superType) {
|
||||||
annotationWrapper.superType = annotationWrapper.redaction ? 'redaction' : annotationWrapper.hint ? 'hint' : 'ignore';
|
annotationWrapper.superType = annotationWrapper.redaction ? 'redaction' : annotationWrapper.hint ? 'hint' : 'ignore';
|
||||||
}
|
}
|
||||||
|
|
||||||
annotationWrapper.typeLabel = 'annotation-type.' + annotationWrapper.superType;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
private static createContent(entry: any) {
|
private static _createContent(entry: any) {
|
||||||
let content = '';
|
let content = '';
|
||||||
if (entry.matchedRule) {
|
if (entry.matchedRule) {
|
||||||
content += 'Rule ' + entry.matchedRule + ' matched \n\n';
|
content += 'Rule ' + entry.matchedRule + ' matched \n\n';
|
||||||
|
|||||||
@ -1,16 +1,9 @@
|
|||||||
import { Comment, IdRemoval, ManualRedactionEntry, ManualRedactions, RedactionLog, RedactionLogEntry, TypeValue, ViewedPages } from '@redaction/red-ui-http';
|
import { IdRemoval, ManualRedactionEntry, ManualRedactions, RedactionLog, RedactionLogEntry, TypeValue, ViewedPages } from '@redaction/red-ui-http';
|
||||||
import { FileStatusWrapper } from './file-status.wrapper';
|
import { FileStatusWrapper } from './file-status.wrapper';
|
||||||
import { UserWrapper } from '../../../user/user.service';
|
import { UserWrapper } from '../../../user/user.service';
|
||||||
import { AnnotationWrapper } from './annotation.wrapper';
|
import { AnnotationWrapper } from './annotation.wrapper';
|
||||||
import { RedactionLogEntryWrapper } from './redaction-log-entry.wrapper';
|
import { RedactionLogEntryWrapper } from './redaction-log-entry.wrapper';
|
||||||
|
|
||||||
export interface AnnotationPair {
|
|
||||||
redactionLogEntry?: RedactionLogEntry;
|
|
||||||
manualRedactionEntry?: ManualRedactionEntry;
|
|
||||||
idRemoval?: IdRemoval;
|
|
||||||
comments?: Comment[];
|
|
||||||
}
|
|
||||||
|
|
||||||
export class FileDataModel {
|
export class FileDataModel {
|
||||||
redactedFileData: Blob;
|
redactedFileData: Blob;
|
||||||
|
|
||||||
@ -47,113 +40,102 @@ export class FileDataModel {
|
|||||||
let result: RedactionLogEntryWrapper[] = [];
|
let result: RedactionLogEntryWrapper[] = [];
|
||||||
|
|
||||||
this.redactionLog.redactionLogEntry.forEach((redactionLogEntry) => {
|
this.redactionLog.redactionLogEntry.forEach((redactionLogEntry) => {
|
||||||
// copy the redactionLog Entry
|
// false positive entries from the redaction-log need to be skipped
|
||||||
const wrapper: RedactionLogEntryWrapper = {};
|
if (redactionLogEntry.type !== 'false_positive') {
|
||||||
Object.assign(wrapper, redactionLogEntry);
|
// copy the redactionLog Entry
|
||||||
wrapper.comments = this.manualRedactions.comments[wrapper.id];
|
const redactionLogEntryWrapper: RedactionLogEntryWrapper = { actionPendingReanalysis: false };
|
||||||
result.push(wrapper);
|
Object.assign(redactionLogEntryWrapper, redactionLogEntry);
|
||||||
|
redactionLogEntryWrapper.comments = this.manualRedactions.comments[redactionLogEntryWrapper.id];
|
||||||
|
result.push(redactionLogEntryWrapper);
|
||||||
|
}
|
||||||
});
|
});
|
||||||
|
|
||||||
this.manualRedactions.entriesToAdd.forEach((manual) => {
|
this.manualRedactions.entriesToAdd.forEach((manual) => {
|
||||||
let wrapper;
|
const markedAsReasonRedactionLogEntry = result.find((r) => r.id === manual.reason);
|
||||||
if (this._hasAlreadyBeenProcessed(manual) && this._isAcceptedOrRejected(manual)) {
|
|
||||||
wrapper = result.find((r) => r.id === manual.id);
|
const relevantRedactionLogEntry = result.find((r) => r.id === manual.id);
|
||||||
if (wrapper) {
|
|
||||||
wrapper.userId = manual.user;
|
// a redaction-log entry is marked as a reason for another entry - hide it
|
||||||
} else {
|
if (!!markedAsReasonRedactionLogEntry) {
|
||||||
wrapper = result.find((r) => r.id === manual.reason);
|
markedAsReasonRedactionLogEntry.hidden = true;
|
||||||
// if we found it
|
|
||||||
if (wrapper) {
|
|
||||||
wrapper.userId = manual.user;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
return;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
// normal case
|
// an entry for this request already exists in the redactionLog
|
||||||
wrapper = result.find((r) => r.id === manual.id);
|
if (!!relevantRedactionLogEntry) {
|
||||||
if (!wrapper) {
|
relevantRedactionLogEntry.userId = manual.user;
|
||||||
// false positive and recommendation case
|
relevantRedactionLogEntry.dictionaryEntry = manual.addToDictionary;
|
||||||
// if we mark Annotation N as false positive -> it's reason is the original annotations Id
|
|
||||||
// if we confirm a recommendation, it's reason is the original annotations Id
|
|
||||||
wrapper = result.find((r) => r.id === manual.reason);
|
|
||||||
// if we found it
|
|
||||||
if (wrapper) {
|
|
||||||
// it's a recommendation if it's not a false positive
|
|
||||||
|
|
||||||
wrapper.recommendation = manual.type !== 'false_positive';
|
// if statuses differ
|
||||||
if (wrapper.recommendation) {
|
if (relevantRedactionLogEntry.status !== manual.status) {
|
||||||
wrapper.pendingRecommendationAnnotationId = manual.id;
|
relevantRedactionLogEntry.actionPendingReanalysis = true;
|
||||||
}
|
relevantRedactionLogEntry.status = manual.status;
|
||||||
wrapper.manual = true;
|
|
||||||
wrapper.manualRedactionType = 'ADD';
|
|
||||||
wrapper.status = manual.status;
|
|
||||||
} else {
|
|
||||||
const dictionary = dictionaryData[manual.type];
|
|
||||||
|
|
||||||
wrapper = {};
|
|
||||||
|
|
||||||
wrapper.id = manual.id;
|
|
||||||
wrapper.dictionaryEntry = manual.addToDictionary;
|
|
||||||
wrapper.legalBasis = manual.legalBasis;
|
|
||||||
wrapper.positions = manual.positions;
|
|
||||||
wrapper.reason = manual.reason;
|
|
||||||
wrapper.status = manual.status;
|
|
||||||
wrapper.type = manual.type;
|
|
||||||
wrapper.userId = manual.user;
|
|
||||||
wrapper.value = manual.value;
|
|
||||||
wrapper.redacted = !dictionary.hint;
|
|
||||||
wrapper.hint = dictionary.hint;
|
|
||||||
wrapper.manualRedactionType = 'ADD';
|
|
||||||
wrapper.manual = true;
|
|
||||||
wrapper.comments = this.manualRedactions.comments[wrapper.id];
|
|
||||||
|
|
||||||
result.push(wrapper);
|
|
||||||
}
|
}
|
||||||
} else {
|
} else {
|
||||||
wrapper.confirmed = true;
|
// dictionary modifying requests that have been processed already updated the dictionary and should not be drawn
|
||||||
|
if (manual.addToDictionary && this._hasAlreadyBeenProcessed(manual)) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
// no entry exists in the redaction log - create it
|
||||||
|
const dictionary = dictionaryData[manual.type];
|
||||||
|
|
||||||
|
const redactionLogEntryWrapper: RedactionLogEntryWrapper = {};
|
||||||
|
|
||||||
|
redactionLogEntryWrapper.id = manual.id;
|
||||||
|
redactionLogEntryWrapper.dictionaryEntry = manual.addToDictionary;
|
||||||
|
redactionLogEntryWrapper.legalBasis = manual.legalBasis;
|
||||||
|
redactionLogEntryWrapper.positions = manual.positions;
|
||||||
|
redactionLogEntryWrapper.reason = manual.reason;
|
||||||
|
redactionLogEntryWrapper.status = manual.status;
|
||||||
|
redactionLogEntryWrapper.type = manual.type;
|
||||||
|
redactionLogEntryWrapper.userId = manual.user;
|
||||||
|
redactionLogEntryWrapper.value = manual.value;
|
||||||
|
redactionLogEntryWrapper.redacted = !dictionary.hint;
|
||||||
|
redactionLogEntryWrapper.hint = dictionary.hint;
|
||||||
|
redactionLogEntryWrapper.manualRedactionType = 'ADD';
|
||||||
|
redactionLogEntryWrapper.manual = true;
|
||||||
|
redactionLogEntryWrapper.comments = this.manualRedactions.comments[redactionLogEntryWrapper.id];
|
||||||
|
|
||||||
|
result.push(redactionLogEntryWrapper);
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
|
|
||||||
this.manualRedactions.idsToRemove.forEach((idToRemove) => {
|
this.manualRedactions.idsToRemove.forEach((idToRemove) => {
|
||||||
let wrapper;
|
const relevantRedactionLogEntry = result.find((r) => r.id === idToRemove.id);
|
||||||
if (this._hasAlreadyBeenProcessed(idToRemove) && this._isAcceptedOrRejected(idToRemove)) {
|
|
||||||
wrapper = result.find((r) => r.id === idToRemove.id);
|
if (!relevantRedactionLogEntry) {
|
||||||
if (wrapper && wrapper.dictionaryEntry) {
|
// idRemove for something that doesn't exist - skip
|
||||||
wrapper.manual = false;
|
|
||||||
wrapper.manualRedactionType = null;
|
|
||||||
wrapper.status = null;
|
|
||||||
}
|
|
||||||
return;
|
return;
|
||||||
}
|
} else {
|
||||||
|
relevantRedactionLogEntry.userId = idToRemove.user;
|
||||||
|
relevantRedactionLogEntry.dictionaryEntry = idToRemove.removeFromDictionary;
|
||||||
|
|
||||||
wrapper = result.find((r) => r.id === idToRemove.id);
|
// if statuses differ
|
||||||
if (wrapper) {
|
if (relevantRedactionLogEntry.status !== idToRemove.status) {
|
||||||
wrapper.manual = true;
|
relevantRedactionLogEntry.actionPendingReanalysis = true;
|
||||||
wrapper.dictionaryEntry = idToRemove.removeFromDictionary;
|
relevantRedactionLogEntry.status = idToRemove.status;
|
||||||
wrapper.userId = idToRemove.user;
|
}
|
||||||
wrapper.status = idToRemove.status;
|
|
||||||
wrapper.manualRedactionType = 'REMOVE';
|
if (this._hasAlreadyBeenProcessed(idToRemove)) {
|
||||||
|
if (idToRemove.status === 'DECLINED') {
|
||||||
|
relevantRedactionLogEntry.status = null;
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
|
|
||||||
result.forEach((id) => {
|
result.forEach((redactionLogEntry) => {
|
||||||
if (id.id === '57205cfd183653d4d159dc8d07f86a4c') {
|
|
||||||
console.log(' ========= ', id);
|
|
||||||
}
|
|
||||||
});
|
|
||||||
|
|
||||||
// remove undone entriesToAdd and idsToRemove
|
|
||||||
result = result.filter((redactionLogEntry) => {
|
|
||||||
if (redactionLogEntry.manual) {
|
if (redactionLogEntry.manual) {
|
||||||
if (redactionLogEntry.manualRedactionType === 'ADD') {
|
if (redactionLogEntry.manualRedactionType === 'ADD') {
|
||||||
const foundManualEntry = this.manualRedactions.entriesToAdd.find((me) => me.id === redactionLogEntry.id);
|
const foundManualEntry = this.manualRedactions.entriesToAdd.find((me) => me.id === redactionLogEntry.id);
|
||||||
|
// ADD has been undone - not yet processed
|
||||||
if (!foundManualEntry) {
|
if (!foundManualEntry) {
|
||||||
return false;
|
redactionLogEntry.hidden = true;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
if (redactionLogEntry.manualRedactionType === 'REMOVE') {
|
if (redactionLogEntry.manualRedactionType === 'REMOVE') {
|
||||||
const foundManualEntry = this.manualRedactions.idsToRemove.find((me) => me.id === redactionLogEntry.id);
|
const foundManualEntry = this.manualRedactions.idsToRemove.find((me) => me.id === redactionLogEntry.id);
|
||||||
|
// REMOVE has been undone - not yet processed
|
||||||
if (!foundManualEntry) {
|
if (!foundManualEntry) {
|
||||||
redactionLogEntry.manual = false;
|
redactionLogEntry.manual = false;
|
||||||
redactionLogEntry.manualRedactionType = null;
|
redactionLogEntry.manualRedactionType = null;
|
||||||
@ -161,14 +143,12 @@ export class FileDataModel {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
return true;
|
|
||||||
});
|
});
|
||||||
|
|
||||||
return result;
|
// remove undone entriesToAdd and idsToRemove
|
||||||
}
|
result = result.filter((redactionLogEntry) => !redactionLogEntry.hidden);
|
||||||
|
|
||||||
private _isAcceptedOrRejected(entry: ManualRedactionEntry | IdRemoval): boolean {
|
return result;
|
||||||
return entry.status === 'APPROVED' || entry.status === 'DECLINED';
|
|
||||||
}
|
}
|
||||||
|
|
||||||
private _hasAlreadyBeenProcessed(entry: ManualRedactionEntry | IdRemoval): boolean {
|
private _hasAlreadyBeenProcessed(entry: ManualRedactionEntry | IdRemoval): boolean {
|
||||||
|
|||||||
@ -11,8 +11,6 @@ export interface RedactionLogEntryWrapper {
|
|||||||
matchedRule?: number;
|
matchedRule?: number;
|
||||||
positions?: Array<Rectangle>;
|
positions?: Array<Rectangle>;
|
||||||
reason?: string;
|
reason?: string;
|
||||||
recommendation?: boolean;
|
|
||||||
pendingRecommendationAnnotationId?: string;
|
|
||||||
redacted?: boolean;
|
redacted?: boolean;
|
||||||
section?: string;
|
section?: string;
|
||||||
sectionNumber?: number;
|
sectionNumber?: number;
|
||||||
@ -21,7 +19,14 @@ export interface RedactionLogEntryWrapper {
|
|||||||
textBefore?: string;
|
textBefore?: string;
|
||||||
type?: string;
|
type?: string;
|
||||||
value?: string;
|
value?: string;
|
||||||
|
|
||||||
|
recommendation?: boolean;
|
||||||
|
recommendationAnnotationId?: string;
|
||||||
|
|
||||||
|
actionPendingReanalysis?: boolean;
|
||||||
|
|
||||||
|
hidden?: boolean;
|
||||||
|
|
||||||
userId?: string;
|
userId?: string;
|
||||||
comments?: Comment[];
|
comments?: Comment[];
|
||||||
confirmed?: boolean;
|
|
||||||
}
|
}
|
||||||
|
|||||||
@ -498,6 +498,11 @@ export class AppStateService {
|
|||||||
type: 'remove-dictionary',
|
type: 'remove-dictionary',
|
||||||
virtual: true
|
virtual: true
|
||||||
};
|
};
|
||||||
|
dictionaryData['remove-only-here'] = {
|
||||||
|
hexColor: '#dd4d50',
|
||||||
|
type: 'remove-only-here',
|
||||||
|
virtual: true
|
||||||
|
};
|
||||||
// generic suggestions
|
// generic suggestions
|
||||||
dictionaryData['suggestion'] = {
|
dictionaryData['suggestion'] = {
|
||||||
hexColor: colors.requestAdd,
|
hexColor: colors.requestAdd,
|
||||||
|
|||||||
@ -454,6 +454,8 @@
|
|||||||
"hints": "Hint Dictionaries"
|
"hints": "Hint Dictionaries"
|
||||||
},
|
},
|
||||||
"annotation-type": {
|
"annotation-type": {
|
||||||
|
"recommendation": "Recommendation",
|
||||||
|
"remove-only-here": "Pending removal ( only here )",
|
||||||
"add-dictionary": "Pending add to dictionary",
|
"add-dictionary": "Pending add to dictionary",
|
||||||
"remove-dictionary": "Pending remove from dictionary",
|
"remove-dictionary": "Pending remove from dictionary",
|
||||||
"suggestion-add-dictionary": "Suggested dictionary add",
|
"suggestion-add-dictionary": "Suggested dictionary add",
|
||||||
|
|||||||
Loading…
x
Reference in New Issue
Block a user