manual annotation workflow

This commit is contained in:
Timo Bejan 2020-11-08 22:19:51 +02:00
parent 241b086601
commit 414d5518dc
19 changed files with 214 additions and 176 deletions

View File

@ -176,12 +176,7 @@ export function HttpLoaderFactory(httpClient: HttpClient) {
component: FilePreviewScreenComponent, component: FilePreviewScreenComponent,
canActivate: [CompositeRouteGuard], canActivate: [CompositeRouteGuard],
data: { data: {
routeGuards: [ routeGuards: [AuthGuard, RedRoleGuard, AppStateGuard]
AuthGuard,
RedRoleGuard,
ProjectMemberGuard,
AppStateGuard
]
} }
} }
] ]

View File

@ -1,7 +1,7 @@
<div <div
[class.hint]="typeValue?.hint" [class.hint]="typeValue?.hint"
[class.request]="typeValue?.type === 'suggestion'" [class.request]="isSuggestionRequest"
[style.background-color]="typeValue?.hexColor" [style.background-color]="color ? color : typeValue?.hexColor"
class="icon" class="icon"
> >
<span>{{ (typeValue?.type)[0] }}</span> <span>{{ (typeValue?.type)[0] }}</span>

View File

@ -12,29 +12,14 @@
text-align: center; text-align: center;
text-transform: uppercase; text-transform: uppercase;
color: $white; color: $white;
position: relative;
} }
.request { .request {
width: 0; transform: rotate(45deg);
height: 0;
border: 9px solid transparent;
position: relative;
top: -9px;
background-color: transparent !important;
&:after {
content: '';
position: absolute;
left: -9px;
top: 9px;
width: 0;
height: 0;
border: 9px solid transparent;
}
span { span {
transform: translateY(9px); transform: rotate(-45deg);
z-index: 2;
} }
} }

View File

@ -6,24 +6,15 @@ import { TypeValue } from '@redaction/red-ui-http';
templateUrl: './annotation-icon.component.html', templateUrl: './annotation-icon.component.html',
styleUrls: ['./annotation-icon.component.scss'] styleUrls: ['./annotation-icon.component.scss']
}) })
export class AnnotationIconComponent implements OnInit { export class AnnotationIconComponent {
@Input() typeValue: TypeValue; @Input() typeValue: TypeValue;
@Input() color: string;
label: string;
constructor() {} constructor() {}
ngOnInit(): void { get isSuggestionRequest() {
if (this.typeValue?.type === 'suggestion') { return (
const styleSheet = document.styleSheets[0]; this.typeValue?.type === 'suggestion' || this.typeValue?.type === 'suggestion-remove'
styleSheet.insertRule( );
`.request:after { border-top-color: ${this.typeValue.hexColor} !important; }`,
styleSheet.cssRules.length
);
styleSheet.insertRule(
`.request { border-bottom-color: ${this.typeValue.hexColor} !important; }`,
styleSheet.cssRules.length
);
}
} }
} }

View File

@ -232,11 +232,13 @@ export class DialogService {
ref.afterClosed().subscribe((result) => { ref.afterClosed().subscribe((result) => {
if (result) { if (result) {
this._manualAnnotationService.removeAnnotation(annotation).subscribe(() => { this._manualAnnotationService
if (callback) { .removeOrSuggestRemoveAnnotation(annotation)
callback(); .subscribe(() => {
} if (callback) {
}); callback();
}
});
} }
}); });

View File

@ -10,19 +10,36 @@
<mat-icon svgIcon="red:check-alt"></mat-icon> <mat-icon svgIcon="red:check-alt"></mat-icon>
</button> </button>
<mat-menu #menu="matMenu" (closed)="onSuggestionMenuClose()" xPosition="before"> <mat-menu #menu="matMenu" (closed)="onSuggestionMenuClose()" xPosition="before">
<div (click)="acceptSuggestion($event, annotation)" mat-menu-item> <div (click)="acceptSuggestion($event, annotation, true)" mat-menu-item>
<redaction-annotation-icon [typeValue]="suggestionType"></redaction-annotation-icon> <redaction-annotation-icon [typeValue]="suggestionType"></redaction-annotation-icon>
<div translate="file-preview.tabs.annotations.accept-suggestion.add-to-dict"></div> <div
[translate]="
annotation.superType === 'suggestion'
? 'file-preview.tabs.annotations.accept-suggestion.add-to-dict'
: 'file-preview.tabs.annotations.accept-suggestion.remove-from-dict'
"
></div>
</div> </div>
<div (click)="acceptSuggestion($event, annotation)" mat-menu-item> <div (click)="acceptSuggestion($event, annotation, false)" mat-menu-item>
<redaction-annotation-icon [typeValue]="suggestionType"></redaction-annotation-icon> <redaction-annotation-icon
[typeValue]="suggestionType"
[color]="'#5B97DB'"
></redaction-annotation-icon>
<div translate="file-preview.tabs.annotations.accept-suggestion.only-here"></div> <div translate="file-preview.tabs.annotations.accept-suggestion.only-here"></div>
</div> </div>
</mat-menu> </mat-menu>
<button
(click)="undoDirectAction($event, annotation)"
*ngIf="annotation.canUndo"
mat-icon-button
>
<mat-icon svgIcon="red:undo"></mat-icon>
</button>
<button <button
(click)="rejectSuggestion($event, annotation)" (click)="rejectSuggestion($event, annotation)"
*ngIf=" *ngIf="
annotation.superType === 'suggestion' || annotation.superType === 'suggestion-remove' !annotation.canUndo &&
(annotation.superType === 'suggestion' || annotation.superType === 'suggestion-remove')
" "
mat-icon-button mat-icon-button
> >
@ -30,7 +47,10 @@
</button> </button>
<button <button
(click)="suggestRemoveAnnotation($event, annotation)" (click)="suggestRemoveAnnotation($event, annotation)"
*ngIf="annotation.superType === 'redaction' || annotation.superType === 'hint'" *ngIf="
!annotation.canUndo &&
(annotation.superType === 'redaction' || annotation.superType === 'hint')
"
mat-icon-button mat-icon-button
> >
<mat-icon svgIcon="red:trash"></mat-icon> <mat-icon svgIcon="red:trash"></mat-icon>

View File

@ -1,7 +1,9 @@
import { Component, Input, OnInit } from '@angular/core'; import { Component, Input, OnInit, Output, EventEmitter } 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 { TypeValue } from '@redaction/red-ui-http';
import { ManualAnnotationService } from '../service/manual-annotation.service';
import { Observable } from 'rxjs';
@Component({ @Component({
selector: 'redaction-annotation-actions', selector: 'redaction-annotation-actions',
@ -12,10 +14,15 @@ export class AnnotationActionsComponent implements OnInit {
@Input() annotation: AnnotationWrapper; @Input() annotation: AnnotationWrapper;
@Input() canPerformAnnotationActions: boolean; @Input() canPerformAnnotationActions: boolean;
@Output() annotationsChanged = new EventEmitter();
suggestionType: TypeValue; suggestionType: TypeValue;
menuOpen: boolean; menuOpen: boolean;
constructor(public appStateService: AppStateService) {} constructor(
public appStateService: AppStateService,
private readonly _manualAnnotationService: ManualAnnotationService
) {}
ngOnInit(): void { ngOnInit(): void {
this.suggestionType = this.appStateService.getDictionaryTypeValue('suggestion'); this.suggestionType = this.appStateService.getDictionaryTypeValue('suggestion');
@ -29,16 +36,39 @@ export class AnnotationActionsComponent implements OnInit {
); );
} }
acceptSuggestion($event: MouseEvent, annotation: AnnotationWrapper) { acceptSuggestion($event: MouseEvent, annotation: AnnotationWrapper, addToDictionary: boolean) {
$event.stopPropagation(); $event.stopPropagation();
this._processObsAndEmit(
this._manualAnnotationService.approveRequest(annotation.id, addToDictionary)
);
} }
rejectSuggestion($event: MouseEvent, annotation: AnnotationWrapper) { rejectSuggestion($event: MouseEvent, annotation: AnnotationWrapper) {
$event.stopPropagation(); $event.stopPropagation();
this._processObsAndEmit(this._manualAnnotationService.declineOrRemoveRequest(annotation));
} }
suggestRemoveAnnotation($event: MouseEvent, annotation: AnnotationWrapper) { suggestRemoveAnnotation($event: MouseEvent, annotation: AnnotationWrapper) {
$event.stopPropagation(); $event.stopPropagation();
this._processObsAndEmit(
this._manualAnnotationService.removeOrSuggestRemoveAnnotation(annotation)
);
}
undoDirectAction($event: MouseEvent, annotation: AnnotationWrapper) {
$event.stopPropagation();
this._processObsAndEmit(this._manualAnnotationService.undoRequest(annotation));
}
private _processObsAndEmit(obs: Observable<any>) {
obs.subscribe(
() => {
this.annotationsChanged.emit();
},
() => {
this.annotationsChanged.emit();
}
);
} }
public openAcceptSuggestionMenu($event: MouseEvent) { public openAcceptSuggestionMenu($event: MouseEvent) {

View File

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

View File

@ -62,9 +62,7 @@ export class FilePreviewScreenComponent implements OnInit {
public viewReady = false; public viewReady = false;
public filters: FilterModel[]; public filters: FilterModel[];
private _activeMenuAnnotation: AnnotationWrapper;
loadingMessage: string; loadingMessage: string;
canPerformAnnotationActions: boolean; canPerformAnnotationActions: boolean;
constructor( constructor(
@ -150,19 +148,20 @@ export class FilePreviewScreenComponent implements OnInit {
AnnotationWrapper.fromManualRedaction( AnnotationWrapper.fromManualRedaction(
mr, mr,
this.fileData.manualRedactions, this.fileData.manualRedactions,
this.appStateService.dictionaryData this.appStateService.dictionaryData,
this.user
) )
); );
const redactionLogAnnotations = this.fileData.redactionLog.redactionLogEntry.map((rde) => const redactionLogAnnotations = this.fileData.redactionLog.redactionLogEntry.map((rde) =>
AnnotationWrapper.fromRedactionLog(rde, this.fileData.manualRedactions) AnnotationWrapper.fromRedactionLog(rde, this.fileData.manualRedactions, this.user)
); );
this.annotations.splice(0, this.annotations.length); //this.annotations.splice(0, this.annotations.length);
this.annotations = [];
this.annotations.push(...manualRedactionAnnotations); this.annotations.push(...manualRedactionAnnotations);
this.annotations.push(...redactionLogAnnotations); this.annotations.push(...redactionLogAnnotations);
this.filters = this._annotationProcessingService.getAnnotationFilter(this.annotations); this.filters = this._annotationProcessingService.getAnnotationFilter(this.annotations);
this.filtersChanged(this.filters); this.filtersChanged(this.filters);
this._changeDetectorRef.detectChanges();
} }
public openFileDetailsDialog($event: MouseEvent) { public openFileDetailsDialog($event: MouseEvent) {
@ -196,7 +195,6 @@ export class FilePreviewScreenComponent implements OnInit {
public assignReviewer() { public assignReviewer() {
this._fileActionService.assignProjectReviewer(null, () => { this._fileActionService.assignProjectReviewer(null, () => {
console.log(this.appStateService.canPerformAnnotationActionsOnCurrentFile());
this.canPerformAnnotationActions = this.appStateService.canPerformAnnotationActionsOnCurrentFile(); this.canPerformAnnotationActions = this.appStateService.canPerformAnnotationActionsOnCurrentFile();
}); });
} }
@ -279,45 +277,6 @@ export class FilePreviewScreenComponent implements OnInit {
} }
} }
public acceptSuggestion($event: MouseEvent, annotation: AnnotationWrapper) {
this.ngZone.run(() => {
this._dialogRef = this._dialogService.openAcceptSuggestionModal(
$event,
annotation,
() => {
this._cleanupAndRedrawManualAnnotations();
}
);
});
}
public rejectSuggestion($event: MouseEvent, annotation: AnnotationWrapper) {
this.ngZone.run(() => {
this._dialogRef = this._dialogService.openRejectSuggestionModal(
$event,
annotation,
() => {
this._cleanupAndRedrawManualAnnotations();
}
);
});
}
suggestRemoveAnnotation($event: MouseEvent, annotation: AnnotationWrapper) {
this.ngZone.run(() => {
this._dialogRef = this._dialogService.openRemoveAnnotationModal(
$event,
annotation,
() => {
annotation.superType = this.appStateService.isActiveProjectOwnerAndManager
? 'ignore'
: 'suggestion-remove';
this._cleanupAndRedrawManualAnnotations();
}
);
});
}
public downloadFile(type: FileType | string) { public downloadFile(type: FileType | string) {
this._fileDownloadService.loadFile(type, this.fileId).subscribe((data) => { this._fileDownloadService.loadFile(type, this.fileId).subscribe((data) => {
saveAs(data, this.appStateService.activeFile.filename); saveAs(data, this.appStateService.activeFile.filename);
@ -485,7 +444,9 @@ export class FilePreviewScreenComponent implements OnInit {
viewerReady($event: WebViewerInstance) { viewerReady($event: WebViewerInstance) {
this.instance = $event; this.instance = $event;
this.viewReady = true; this.viewReady = true;
this._reanalyseTooltip.show(); if (this._reanalyseTooltip) {
this._reanalyseTooltip.show();
}
this._cleanupAndRedrawManualAnnotations(); this._cleanupAndRedrawManualAnnotations();
} }
@ -494,7 +455,7 @@ export class FilePreviewScreenComponent implements OnInit {
this.annotations, this.annotations,
filters filters
); );
this._changeDetectorRef.detectChanges(); this._changeDetectorRef.markForCheck();
} }
preventArrowDefault($event: KeyboardEvent) { preventArrowDefault($event: KeyboardEvent) {
@ -507,8 +468,6 @@ export class FilePreviewScreenComponent implements OnInit {
this._fileDownloadService this._fileDownloadService
.loadActiveFileManualAnnotations() .loadActiveFileManualAnnotations()
.subscribe((manualRedactions) => { .subscribe((manualRedactions) => {
this.fileData.manualRedactions = manualRedactions;
const annotationsToRemove = []; const annotationsToRemove = [];
this.fileData.entriesToAdd.forEach((manuallyAddedEntry) => { this.fileData.entriesToAdd.forEach((manuallyAddedEntry) => {
const annotation = this.activeViewer.annotManager.getAnnotationById( const annotation = this.activeViewer.annotManager.getAnnotationById(
@ -519,6 +478,8 @@ export class FilePreviewScreenComponent implements OnInit {
} }
}); });
this.activeViewer.annotManager.deleteAnnotations(annotationsToRemove, false, true); this.activeViewer.annotManager.deleteAnnotations(annotationsToRemove, false, true);
this.fileData.manualRedactions = manualRedactions;
this._annotationDrawService.drawAnnotations( this._annotationDrawService.drawAnnotations(
this.instance, this.instance,
this.fileData.entriesToAdd this.fileData.entriesToAdd
@ -558,4 +519,8 @@ export class FilePreviewScreenComponent implements OnInit {
$event.stopPropagation(); $event.stopPropagation();
this._fileActionService.undoApproveOrUnderApproval().subscribe(() => {}); this._fileActionService.undoApproveOrUnderApproval().subscribe(() => {});
} }
annotationsChangedByReviewAction() {
this._cleanupAndRedrawManualAnnotations();
}
} }

View File

@ -4,13 +4,15 @@ import {
ManualRedactions, ManualRedactions,
Point, Point,
RedactionLogEntry, RedactionLogEntry,
TypeValue TypeValue,
User
} from '@redaction/red-ui-http'; } from '@redaction/red-ui-http';
import { UserWrapper } from '../../../user/user.service';
export const SuperTypeSorter = { export const SuperTypeSorter = {
redaction: 1, redaction: 1,
request: 2, suggestion: 2,
'request-remove': 3, 'suggestion-remove': 3,
hint: 4, hint: 4,
ignore: 5 ignore: 5
}; };
@ -26,11 +28,13 @@ export class AnnotationWrapper {
content: string; content: string;
manual: boolean; manual: boolean;
userId: string; userId: string;
canUndo: boolean;
pageNumber; pageNumber;
static fromRedactionLog( static fromRedactionLog(
redactionLogEntry: RedactionLogEntry, redactionLogEntry: RedactionLogEntry,
manualRedactions: ManualRedactions manualRedactions: ManualRedactions,
user: UserWrapper
) { ) {
const comments: { [key: string]: Array<Comment> } = manualRedactions.comments; const comments: { [key: string]: Array<Comment> } = manualRedactions.comments;
const annotationWrapper = new AnnotationWrapper(); const annotationWrapper = new AnnotationWrapper();
@ -41,7 +45,6 @@ export class AnnotationWrapper {
? 'hint' ? 'hint'
: 'ignore'; : 'ignore';
AnnotationWrapper._handleRemoveSuperType(annotationWrapper, manualRedactions);
annotationWrapper.dictionary = redactionLogEntry.type; annotationWrapper.dictionary = redactionLogEntry.type;
annotationWrapper.firstTopLeftPoint = redactionLogEntry.positions[0]?.topLeft; annotationWrapper.firstTopLeftPoint = redactionLogEntry.positions[0]?.topLeft;
annotationWrapper.pageNumber = redactionLogEntry.positions[0]?.page; annotationWrapper.pageNumber = redactionLogEntry.positions[0]?.page;
@ -50,6 +53,8 @@ export class AnnotationWrapper {
annotationWrapper.superType === 'redaction' || annotationWrapper.superType === 'ignore' annotationWrapper.superType === 'redaction' || annotationWrapper.superType === 'ignore'
? AnnotationWrapper.createAnnotationContentForRedactionLogEntry(redactionLogEntry) ? AnnotationWrapper.createAnnotationContentForRedactionLogEntry(redactionLogEntry)
: null; : null;
AnnotationWrapper._handleRemoveSuperType(annotationWrapper, manualRedactions, user);
annotationWrapper.comments = comments[redactionLogEntry.id] annotationWrapper.comments = comments[redactionLogEntry.id]
? comments[redactionLogEntry.id] ? comments[redactionLogEntry.id]
: []; : [];
@ -59,7 +64,8 @@ export class AnnotationWrapper {
static fromManualRedaction( static fromManualRedaction(
manualRedactionEntry: ManualRedactionEntry, manualRedactionEntry: ManualRedactionEntry,
manualRedactions: ManualRedactions, manualRedactions: ManualRedactions,
dictionaryData: { [p: string]: TypeValue } dictionaryData: { [p: string]: TypeValue },
user: UserWrapper
) { ) {
const comments: { [key: string]: Array<Comment> } = manualRedactions.comments; const comments: { [key: string]: Array<Comment> } = manualRedactions.comments;
const annotationWrapper = new AnnotationWrapper(); const annotationWrapper = new AnnotationWrapper();
@ -69,7 +75,7 @@ export class AnnotationWrapper {
dictionaryData dictionaryData
); );
AnnotationWrapper._handleRemoveSuperType(annotationWrapper, manualRedactions); AnnotationWrapper._handleRemoveSuperType(annotationWrapper, manualRedactions, user);
annotationWrapper.dictionary = manualRedactionEntry.type; annotationWrapper.dictionary = manualRedactionEntry.type;
annotationWrapper.firstTopLeftPoint = manualRedactionEntry.positions[0]?.topLeft; annotationWrapper.firstTopLeftPoint = manualRedactionEntry.positions[0]?.topLeft;
annotationWrapper.pageNumber = manualRedactionEntry.positions[0]?.page; annotationWrapper.pageNumber = manualRedactionEntry.positions[0]?.page;
@ -81,12 +87,16 @@ export class AnnotationWrapper {
? comments[manualRedactionEntry.id] ? comments[manualRedactionEntry.id]
: []; : [];
annotationWrapper.userId = manualRedactionEntry.user; annotationWrapper.userId = manualRedactionEntry.user;
annotationWrapper.canUndo =
!manualRedactionEntry.processedDate && manualRedactionEntry.user === user.id;
return annotationWrapper; return annotationWrapper;
} }
private static _handleRemoveSuperType( private static _handleRemoveSuperType(
annotationWrapper: AnnotationWrapper, annotationWrapper: AnnotationWrapper,
manualRedactions: ManualRedactions manualRedactions: ManualRedactions,
user: UserWrapper
) { ) {
const toRemove = manualRedactions.idsToRemove.find( const toRemove = manualRedactions.idsToRemove.find(
(trm) => trm.id === annotationWrapper.id (trm) => trm.id === annotationWrapper.id
@ -100,6 +110,11 @@ export class AnnotationWrapper {
? 'ignore' ? 'ignore'
: annotationWrapper.superType : annotationWrapper.superType
: annotationWrapper.superType; : annotationWrapper.superType;
if (toRemove) {
annotationWrapper.canUndo = !toRemove.processedDate && toRemove.user === user.id;
console.log(annotationWrapper.canUndo, toRemove.user, user.id);
}
} }
static getManualRedactionSuperType( static getManualRedactionSuperType(

View File

@ -17,12 +17,15 @@ export class FileDataModel {
) {} ) {}
get entriesToAdd(): ManualRedactionEntry[] { get entriesToAdd(): ManualRedactionEntry[] {
return this.manualRedactions.entriesToAdd.filter( return this.manualRedactions.entriesToAdd.filter((e) => {
(e) => const notDeclined = e.status !== 'DECLINED';
e.status !== 'DECLINED' && const notAlreadyDrawn = !this.redactionLog.redactionLogEntry.find((r) => r.id === e.id);
!this.redactionLog.redactionLogEntry.find((r) => r.id === e.id) && const alreadyProcessed =
!!e.processedDate &&
new Date(e.processedDate).getTime() < new Date(e.processedDate).getTime() <
new Date(this.fileStatus.lastProcessed).getTime() new Date(this.fileStatus.lastProcessed).getTime();
);
return notDeclined && notAlreadyDrawn && !alreadyProcessed;
});
} }
} }

View File

@ -68,7 +68,9 @@ export class AnnotationProcessingService {
for (const filter of flatFilters) { for (const filter of flatFilters) {
if ( if (
filter.checked && filter.checked &&
(filter.key === annotation.dictionary || ((filter.key === annotation.dictionary &&
(annotation.superType === 'hint' ||
annotation.superType === 'redaction')) ||
filter.key === annotation.superType) filter.key === annotation.superType)
) { ) {
found = true; found = true;

View File

@ -58,10 +58,11 @@ export class ManualAnnotationService {
// this wraps // this wraps
// /manualRedaction/approve // /manualRedaction/approve
public approveRequest(annotationId: string) { public approveRequest(annotationId: string, addToDictionary: boolean = false) {
// for only here - approve the request // for only here - approve the request
return this._manualRedactionControllerService return this._manualRedactionControllerService
.approveRequest( .approveRequest(
{ addOrRemoveFromDictionary: addToDictionary },
this._appStateService.activeProjectId, this._appStateService.activeProjectId,
this._appStateService.activeFile.fileId, this._appStateService.activeFile.fileId,
annotationId annotationId
@ -78,6 +79,26 @@ export class ManualAnnotationService {
); );
} }
undoRequest(annotationWrapper: AnnotationWrapper) {
return this._manualRedactionControllerService
.undo(
this._appStateService.activeProjectId,
this._appStateService.activeFileId,
annotationWrapper.id
)
.pipe(
tap(
() => this._notify('manual-annotation.undo-request.success'),
() => {
this._notify(
'manual-annotation.undo-request.error',
NotificationType.ERROR
);
}
)
);
}
// this wraps // this wraps
// /manualRedaction/decline/remove // /manualRedaction/decline/remove
// /manualRedaction/undo // /manualRedaction/undo
@ -124,7 +145,10 @@ export class ManualAnnotationService {
// this wraps // this wraps
// /manualRedaction/redaction/remove/ // /manualRedaction/redaction/remove/
// /manualRedaction/request/remove/ // /manualRedaction/request/remove/
removeAnnotation(annotationWrapper: AnnotationWrapper, removeFromDictionary: boolean = false) { removeOrSuggestRemoveAnnotation(
annotationWrapper: AnnotationWrapper,
removeFromDictionary: boolean = false
) {
if (this._appStateService.isActiveProjectOwnerAndManager) { if (this._appStateService.isActiveProjectOwnerAndManager) {
return this._manualRedactionControllerService return this._manualRedactionControllerService
.removeRedaction( .removeRedaction(

View File

@ -14,7 +14,7 @@ import { NotificationService, NotificationType } from '../notification/notificat
import { TranslateService } from '@ngx-translate/core'; import { TranslateService } from '@ngx-translate/core';
import { Router } from '@angular/router'; import { Router } from '@angular/router';
import { UserService, UserWrapper } from '../user/user.service'; import { UserService, UserWrapper } from '../user/user.service';
import { forkJoin, interval, of } from 'rxjs'; import { forkJoin, interval, of, timer } from 'rxjs';
import { tap } from 'rxjs/operators'; import { tap } from 'rxjs/operators';
import { download } from '../utils/file-download-utils'; import { download } from '../utils/file-download-utils';
import { humanize } from '../utils/functions'; import { humanize } from '../utils/functions';
@ -128,7 +128,7 @@ export class AppStateService {
activeFile: null activeFile: null
}; };
interval(5000) timer(5000, 5000)
.pipe( .pipe(
tap(() => { tap(() => {
this.reloadActiveProjectFiles(); this.reloadActiveProjectFiles();
@ -136,7 +136,7 @@ export class AppStateService {
) )
.subscribe(); .subscribe();
interval(5000) timer(5000, 5000)
.pipe( .pipe(
tap(() => { tap(() => {
this.updateDictionaryVersion(); this.updateDictionaryVersion();
@ -144,7 +144,7 @@ export class AppStateService {
) )
.subscribe(); .subscribe();
interval(30000) timer(5000, 30000)
.pipe( .pipe(
tap(() => { tap(() => {
this.loadAllProjects(); this.loadAllProjects();
@ -597,7 +597,7 @@ export class AppStateService {
(fileStatus.status === 'UNASSIGNED' || (fileStatus.status === 'UNASSIGNED' ||
fileStatus.status === 'UNDER_REVIEW' || fileStatus.status === 'UNDER_REVIEW' ||
fileStatus.status === 'UNDER_APPROVAL') && fileStatus.status === 'UNDER_APPROVAL') &&
fileStatus.dictionaryVersion !== this.dictionaryVersion (fileStatus.dictionaryVersion !== this.dictionaryVersion || fileStatus.hasRequests)
); );
} }

View File

@ -257,6 +257,7 @@
"label": "Workload", "label": "Workload",
"accept-suggestion": { "accept-suggestion": {
"add-to-dict": "Approve and add to dictionary", "add-to-dict": "Approve and add to dictionary",
"remove-from-dict": "Approve and remove from dictionary",
"only-here": "Approve only here" "only-here": "Approve only here"
} }
} }
@ -316,8 +317,8 @@
"super-type": { "super-type": {
"redaction": "Redaction", "redaction": "Redaction",
"hint": "Hint", "hint": "Hint",
"request": "Redaction Request", "suggestion": "Suggestion",
"request-remove": "Remove Redaction Request", "suggestion-remove": "Suggestion to Remove",
"ignore": "Ignored redaction" "ignore": "Ignored redaction"
} }
}, },

View File

@ -17,6 +17,7 @@ import { Observable } from 'rxjs';
import { AddCommentRequest } from '../model/addCommentRequest'; import { AddCommentRequest } from '../model/addCommentRequest';
import { AddRedactionRequest } from '../model/addRedactionRequest'; import { AddRedactionRequest } from '../model/addRedactionRequest';
import { ApproveRequest } from '../model/approveRequest';
import { CommentResponse } from '../model/commentResponse'; import { CommentResponse } from '../model/commentResponse';
import { ManualAddResponse } from '../model/manualAddResponse'; import { ManualAddResponse } from '../model/manualAddResponse';
import { ManualRedactions } from '../model/manualRedactions'; import { ManualRedactions } from '../model/manualRedactions';
@ -27,9 +28,9 @@ import { Configuration } from '../configuration';
@Injectable() @Injectable()
export class ManualRedactionControllerService { export class ManualRedactionControllerService {
protected basePath = '';
public defaultHeaders = new HttpHeaders(); public defaultHeaders = new HttpHeaders();
public configuration = new Configuration(); public configuration = new Configuration();
protected basePath = '';
constructor( constructor(
protected httpClient: HttpClient, protected httpClient: HttpClient,
@ -45,6 +46,20 @@ export class ManualRedactionControllerService {
} }
} }
/**
* @param consumes string[] mime-types
* @return true: consumes contains 'multipart/form-data', false: otherwise
*/
private canConsumeForm(consumes: string[]): boolean {
const form = 'multipart/form-data';
for (const consume of consumes) {
if (form === consume) {
return true;
}
}
return false;
}
/** /**
* Adds a comment to a redaction/redaction request * Adds a comment to a redaction/redaction request
* None * None
@ -63,7 +78,6 @@ export class ManualRedactionControllerService {
observe?: 'body', observe?: 'body',
reportProgress?: boolean reportProgress?: boolean
): Observable<CommentResponse>; ): Observable<CommentResponse>;
public addComment( public addComment(
body: AddCommentRequest, body: AddCommentRequest,
projectId: string, projectId: string,
@ -72,7 +86,6 @@ export class ManualRedactionControllerService {
observe?: 'response', observe?: 'response',
reportProgress?: boolean reportProgress?: boolean
): Observable<HttpResponse<CommentResponse>>; ): Observable<HttpResponse<CommentResponse>>;
public addComment( public addComment(
body: AddCommentRequest, body: AddCommentRequest,
projectId: string, projectId: string,
@ -81,7 +94,6 @@ export class ManualRedactionControllerService {
observe?: 'events', observe?: 'events',
reportProgress?: boolean reportProgress?: boolean
): Observable<HttpEvent<CommentResponse>>; ): Observable<HttpEvent<CommentResponse>>;
public addComment( public addComment(
body: AddCommentRequest, body: AddCommentRequest,
projectId: string, projectId: string,
@ -174,7 +186,6 @@ export class ManualRedactionControllerService {
observe?: 'body', observe?: 'body',
reportProgress?: boolean reportProgress?: boolean
): Observable<ManualAddResponse>; ): Observable<ManualAddResponse>;
public addRedaction( public addRedaction(
body: AddRedactionRequest, body: AddRedactionRequest,
projectId: string, projectId: string,
@ -182,7 +193,6 @@ export class ManualRedactionControllerService {
observe?: 'response', observe?: 'response',
reportProgress?: boolean reportProgress?: boolean
): Observable<HttpResponse<ManualAddResponse>>; ): Observable<HttpResponse<ManualAddResponse>>;
public addRedaction( public addRedaction(
body: AddRedactionRequest, body: AddRedactionRequest,
projectId: string, projectId: string,
@ -190,7 +200,6 @@ export class ManualRedactionControllerService {
observe?: 'events', observe?: 'events',
reportProgress?: boolean reportProgress?: boolean
): Observable<HttpEvent<ManualAddResponse>>; ): Observable<HttpEvent<ManualAddResponse>>;
public addRedaction( public addRedaction(
body: AddRedactionRequest, body: AddRedactionRequest,
projectId: string, projectId: string,
@ -263,6 +272,7 @@ export class ManualRedactionControllerService {
/** /**
* Approves a redaction request/ remove redaction request * Approves a redaction request/ remove redaction request
* None * None
* @param body approveRequest
* @param projectId projectId * @param projectId projectId
* @param fileId fileId * @param fileId fileId
* @param annotationId annotationId * @param annotationId annotationId
@ -270,36 +280,43 @@ export class ManualRedactionControllerService {
* @param reportProgress flag to report request and response progress. * @param reportProgress flag to report request and response progress.
*/ */
public approveRequest( public approveRequest(
body: ApproveRequest,
projectId: string, projectId: string,
fileId: string, fileId: string,
annotationId: string, annotationId: string,
observe?: 'body', observe?: 'body',
reportProgress?: boolean reportProgress?: boolean
): Observable<any>; ): Observable<any>;
public approveRequest( public approveRequest(
body: ApproveRequest,
projectId: string, projectId: string,
fileId: string, fileId: string,
annotationId: string, annotationId: string,
observe?: 'response', observe?: 'response',
reportProgress?: boolean reportProgress?: boolean
): Observable<HttpResponse<any>>; ): Observable<HttpResponse<any>>;
public approveRequest( public approveRequest(
body: ApproveRequest,
projectId: string, projectId: string,
fileId: string, fileId: string,
annotationId: string, annotationId: string,
observe?: 'events', observe?: 'events',
reportProgress?: boolean reportProgress?: boolean
): Observable<HttpEvent<any>>; ): Observable<HttpEvent<any>>;
public approveRequest( public approveRequest(
body: ApproveRequest,
projectId: string, projectId: string,
fileId: string, fileId: string,
annotationId: string, annotationId: string,
observe: any = 'body', observe: any = 'body',
reportProgress: boolean = false reportProgress: boolean = false
): Observable<any> { ): Observable<any> {
if (body === null || body === undefined) {
throw new Error(
'Required parameter body was null or undefined when calling approveRequest.'
);
}
if (projectId === null || projectId === undefined) { if (projectId === null || projectId === undefined) {
throw new Error( throw new Error(
'Required parameter projectId was null or undefined when calling approveRequest.' 'Required parameter projectId was null or undefined when calling approveRequest.'
@ -339,7 +356,13 @@ export class ManualRedactionControllerService {
} }
// to determine the Content-Type header // to determine the Content-Type header
const consumes: string[] = []; const consumes: string[] = ['application/json'];
const httpContentTypeSelected:
| string
| undefined = this.configuration.selectHeaderContentType(consumes);
if (httpContentTypeSelected !== undefined) {
headers = headers.set('Content-Type', httpContentTypeSelected);
}
return this.httpClient.request<any>( return this.httpClient.request<any>(
'post', 'post',
@ -347,6 +370,7 @@ export class ManualRedactionControllerService {
String(projectId) String(projectId)
)}/${encodeURIComponent(String(fileId))}/${encodeURIComponent(String(annotationId))}`, )}/${encodeURIComponent(String(fileId))}/${encodeURIComponent(String(annotationId))}`,
{ {
body: body,
withCredentials: this.configuration.withCredentials, withCredentials: this.configuration.withCredentials,
headers: headers, headers: headers,
observe: observe, observe: observe,
@ -371,7 +395,6 @@ export class ManualRedactionControllerService {
observe?: 'body', observe?: 'body',
reportProgress?: boolean reportProgress?: boolean
): Observable<any>; ): Observable<any>;
public declineRequest( public declineRequest(
projectId: string, projectId: string,
fileId: string, fileId: string,
@ -379,7 +402,6 @@ export class ManualRedactionControllerService {
observe?: 'response', observe?: 'response',
reportProgress?: boolean reportProgress?: boolean
): Observable<HttpResponse<any>>; ): Observable<HttpResponse<any>>;
public declineRequest( public declineRequest(
projectId: string, projectId: string,
fileId: string, fileId: string,
@ -387,7 +409,6 @@ export class ManualRedactionControllerService {
observe?: 'events', observe?: 'events',
reportProgress?: boolean reportProgress?: boolean
): Observable<HttpEvent<any>>; ): Observable<HttpEvent<any>>;
public declineRequest( public declineRequest(
projectId: string, projectId: string,
fileId: string, fileId: string,
@ -464,21 +485,18 @@ export class ManualRedactionControllerService {
observe?: 'body', observe?: 'body',
reportProgress?: boolean reportProgress?: boolean
): Observable<ManualRedactions>; ): Observable<ManualRedactions>;
public getManualRedaction( public getManualRedaction(
projectId: string, projectId: string,
fileId: string, fileId: string,
observe?: 'response', observe?: 'response',
reportProgress?: boolean reportProgress?: boolean
): Observable<HttpResponse<ManualRedactions>>; ): Observable<HttpResponse<ManualRedactions>>;
public getManualRedaction( public getManualRedaction(
projectId: string, projectId: string,
fileId: string, fileId: string,
observe?: 'events', observe?: 'events',
reportProgress?: boolean reportProgress?: boolean
): Observable<HttpEvent<ManualRedactions>>; ): Observable<HttpEvent<ManualRedactions>>;
public getManualRedaction( public getManualRedaction(
projectId: string, projectId: string,
fileId: string, fileId: string,
@ -533,7 +551,6 @@ export class ManualRedactionControllerService {
} }
); );
} }
/** /**
* Removes a redaction * Removes a redaction
* None * None
@ -550,7 +567,6 @@ export class ManualRedactionControllerService {
observe?: 'body', observe?: 'body',
reportProgress?: boolean reportProgress?: boolean
): Observable<ManualAddResponse>; ): Observable<ManualAddResponse>;
public removeRedaction( public removeRedaction(
body: RemoveRedactionRequest, body: RemoveRedactionRequest,
projectId: string, projectId: string,
@ -558,7 +574,6 @@ export class ManualRedactionControllerService {
observe?: 'response', observe?: 'response',
reportProgress?: boolean reportProgress?: boolean
): Observable<HttpResponse<ManualAddResponse>>; ): Observable<HttpResponse<ManualAddResponse>>;
public removeRedaction( public removeRedaction(
body: RemoveRedactionRequest, body: RemoveRedactionRequest,
projectId: string, projectId: string,
@ -566,7 +581,6 @@ export class ManualRedactionControllerService {
observe?: 'events', observe?: 'events',
reportProgress?: boolean reportProgress?: boolean
): Observable<HttpEvent<ManualAddResponse>>; ): Observable<HttpEvent<ManualAddResponse>>;
public removeRedaction( public removeRedaction(
body: RemoveRedactionRequest, body: RemoveRedactionRequest,
projectId: string, projectId: string,
@ -652,7 +666,6 @@ export class ManualRedactionControllerService {
observe?: 'body', observe?: 'body',
reportProgress?: boolean reportProgress?: boolean
): Observable<ManualAddResponse>; ): Observable<ManualAddResponse>;
public requestAddRedaction( public requestAddRedaction(
body: AddRedactionRequest, body: AddRedactionRequest,
projectId: string, projectId: string,
@ -660,7 +673,6 @@ export class ManualRedactionControllerService {
observe?: 'response', observe?: 'response',
reportProgress?: boolean reportProgress?: boolean
): Observable<HttpResponse<ManualAddResponse>>; ): Observable<HttpResponse<ManualAddResponse>>;
public requestAddRedaction( public requestAddRedaction(
body: AddRedactionRequest, body: AddRedactionRequest,
projectId: string, projectId: string,
@ -668,7 +680,6 @@ export class ManualRedactionControllerService {
observe?: 'events', observe?: 'events',
reportProgress?: boolean reportProgress?: boolean
): Observable<HttpEvent<ManualAddResponse>>; ): Observable<HttpEvent<ManualAddResponse>>;
public requestAddRedaction( public requestAddRedaction(
body: AddRedactionRequest, body: AddRedactionRequest,
projectId: string, projectId: string,
@ -754,7 +765,6 @@ export class ManualRedactionControllerService {
observe?: 'body', observe?: 'body',
reportProgress?: boolean reportProgress?: boolean
): Observable<ManualAddResponse>; ): Observable<ManualAddResponse>;
public requestRemoveRedaction( public requestRemoveRedaction(
body: RemoveRedactionRequest, body: RemoveRedactionRequest,
projectId: string, projectId: string,
@ -762,7 +772,6 @@ export class ManualRedactionControllerService {
observe?: 'response', observe?: 'response',
reportProgress?: boolean reportProgress?: boolean
): Observable<HttpResponse<ManualAddResponse>>; ): Observable<HttpResponse<ManualAddResponse>>;
public requestRemoveRedaction( public requestRemoveRedaction(
body: RemoveRedactionRequest, body: RemoveRedactionRequest,
projectId: string, projectId: string,
@ -770,7 +779,6 @@ export class ManualRedactionControllerService {
observe?: 'events', observe?: 'events',
reportProgress?: boolean reportProgress?: boolean
): Observable<HttpEvent<ManualAddResponse>>; ): Observable<HttpEvent<ManualAddResponse>>;
public requestRemoveRedaction( public requestRemoveRedaction(
body: RemoveRedactionRequest, body: RemoveRedactionRequest,
projectId: string, projectId: string,
@ -856,7 +864,6 @@ export class ManualRedactionControllerService {
observe?: 'body', observe?: 'body',
reportProgress?: boolean reportProgress?: boolean
): Observable<any>; ): Observable<any>;
public undo( public undo(
projectId: string, projectId: string,
fileId: string, fileId: string,
@ -864,7 +871,6 @@ export class ManualRedactionControllerService {
observe?: 'response', observe?: 'response',
reportProgress?: boolean reportProgress?: boolean
): Observable<HttpResponse<any>>; ): Observable<HttpResponse<any>>;
public undo( public undo(
projectId: string, projectId: string,
fileId: string, fileId: string,
@ -872,7 +878,6 @@ export class ManualRedactionControllerService {
observe?: 'events', observe?: 'events',
reportProgress?: boolean reportProgress?: boolean
): Observable<HttpEvent<any>>; ): Observable<HttpEvent<any>>;
public undo( public undo(
projectId: string, projectId: string,
fileId: string, fileId: string,
@ -951,7 +956,6 @@ export class ManualRedactionControllerService {
observe?: 'body', observe?: 'body',
reportProgress?: boolean reportProgress?: boolean
): Observable<any>; ): Observable<any>;
public undoComment( public undoComment(
projectId: string, projectId: string,
fileId: string, fileId: string,
@ -960,7 +964,6 @@ export class ManualRedactionControllerService {
observe?: 'response', observe?: 'response',
reportProgress?: boolean reportProgress?: boolean
): Observable<HttpResponse<any>>; ): Observable<HttpResponse<any>>;
public undoComment( public undoComment(
projectId: string, projectId: string,
fileId: string, fileId: string,
@ -969,7 +972,6 @@ export class ManualRedactionControllerService {
observe?: 'events', observe?: 'events',
reportProgress?: boolean reportProgress?: boolean
): Observable<HttpEvent<any>>; ): Observable<HttpEvent<any>>;
public undoComment( public undoComment(
projectId: string, projectId: string,
fileId: string, fileId: string,
@ -1040,18 +1042,4 @@ export class ManualRedactionControllerService {
} }
); );
} }
/**
* @param consumes string[] mime-types
* @return true: consumes contains 'multipart/form-data', false: otherwise
*/
private canConsumeForm(consumes: string[]): boolean {
const form = 'multipart/form-data';
for (const consume of consumes) {
if (form === consume) {
return true;
}
}
return false;
}
} }

View File

@ -0,0 +1,15 @@
/**
* API Documentation for Redaction Gateway
* Description for redaction
*
* OpenAPI spec version: 1.0
*
*
* NOTE: This class is auto generated by the swagger code generator program.
* https://github.com/swagger-api/swagger-codegen.git
* Do not edit the class manually.
*/
export interface ApproveRequest {
addOrRemoveFromDictionary?: boolean;
}

View File

@ -34,3 +34,4 @@ export * from './userResponse';
export * from './versionsResponse'; export * from './versionsResponse';
export * from './viewedPages'; export * from './viewedPages';
export * from './viewedPagesRequest'; export * from './viewedPagesRequest';
export * from './approveRequest';