Reworked flows for add request and add redaction for user / manager

This commit is contained in:
Timo Bejan 2020-10-30 09:59:23 +02:00
parent 30c15f2e60
commit 615462893f
15 changed files with 434 additions and 274 deletions

View File

@ -47,7 +47,6 @@ import { LogoComponent } from './logo/logo.component';
import { CompositeRouteGuard } from './utils/composite-route.guard';
import { AppStateGuard } from './state/app-state.guard';
import { SimpleDoughnutChartComponent } from './components/simple-doughnut-chart/simple-doughnut-chart.component';
import { ManualRedactionDialogComponent } from './dialogs/manual-redaction-dialog/manual-redaction-dialog.component';
import { MatCheckboxModule } from '@angular/material/checkbox';
import { MatSlideToggleModule } from '@angular/material/slide-toggle';
import { AnnotationIconComponent } from './components/annotation-icon/annotation-icon.component';
@ -61,6 +60,7 @@ import { MatNativeDateModule } from '@angular/material/core';
import { MatInputModule } from '@angular/material/input';
import { ProjectMemberGuard } from './auth/project-member-guard.service';
import { HumanizePipe } from './utils/humanize.pipe';
import { ManualAnnotationDialogComponent } from './dialogs/manual-redaction-dialog/manual-annotation-dialog.component';
export function HttpLoaderFactory(httpClient: HttpClient) {
return new TranslateHttpLoader(httpClient, '/assets/i18n/', '.json');
@ -83,7 +83,7 @@ export function HttpLoaderFactory(httpClient: HttpClient) {
StatusBarComponent,
LogoComponent,
SimpleDoughnutChartComponent,
ManualRedactionDialogComponent,
ManualAnnotationDialogComponent,
AnnotationIconComponent,
AuthErrorComponent,
HumanizePipe

View File

@ -5,7 +5,6 @@ import {
FileStatus,
FileUploadControllerService,
ManualRedactionControllerService,
ManualRedactionEntry,
Project
} from '@redaction/red-ui-http';
import { ConfirmationDialogComponent } from '../common/confirmation-dialog/confirmation-dialog.component';
@ -14,10 +13,10 @@ import { TranslateService } from '@ngx-translate/core';
import { AppStateService } from '../state/app-state.service';
import { AddEditProjectDialogComponent } from './add-edit-project-dialog/add-edit-project-dialog.component';
import { AssignOwnerDialogComponent } from './assign-owner-dialog/assign-owner-dialog.component';
import { ManualRedactionDialogComponent } from './manual-redaction-dialog/manual-redaction-dialog.component';
import { Annotations } from '@pdftron/webviewer';
import { ManualRedactionEntryWrapper } from '../screens/file/model/manual-redaction-entry.wrapper';
import { AnnotationWrapper } from '../screens/file/model/annotation.wrapper';
import { ManualAnnotationDialogComponent } from './manual-redaction-dialog/manual-annotation-dialog.component';
import { ManualAnnotationService } from '../screens/file/service/manual-annotation.service';
const dialogConfig = {
width: '600px',
@ -35,6 +34,7 @@ export class DialogService {
private readonly _appStateService: AppStateService,
private readonly _fileUploadControllerService: FileUploadControllerService,
private readonly _notificationService: NotificationService,
private readonly _manualAnnotationService: ManualAnnotationService,
private readonly _manualRedactionControllerService: ManualRedactionControllerService
) {}
@ -83,8 +83,8 @@ export class DialogService {
public openManualRedactionDialog(
$event: ManualRedactionEntryWrapper,
cb?: Function
): MatDialogRef<ManualRedactionDialogComponent> {
const ref = this._dialog.open(ManualRedactionDialogComponent, {
): MatDialogRef<ManualAnnotationDialogComponent> {
const ref = this._dialog.open(ManualAnnotationDialogComponent, {
...dialogConfig,
autoFocus: true,
data: $event
@ -99,51 +99,25 @@ export class DialogService {
return ref;
}
public acceptSuggestionAnnotation(
public acceptSuggestion(
$event: MouseEvent,
annotation: AnnotationWrapper,
projectId: string,
fileId: string
annotation: AnnotationWrapper
): MatDialogRef<ConfirmationDialogComponent> {
$event.stopPropagation();
const ref = this._dialog.open(ConfirmationDialogComponent, dialogConfig);
ref.afterClosed().subscribe((result) => {
if (result) {
this._manualRedactionControllerService
.approveRequest(projectId, fileId, annotation.uuid)
.subscribe(
() => {
this._notificationService.showToastNotification(
this._translateService.instant(
'manual-redaction.confirm-annotation.success.label'
),
null,
NotificationType.SUCCESS
);
},
(err) => {
this._notificationService.showToastNotification(
this._translateService.instant(
'manual-redaction.confirm-annotation.failed.label',
err
),
null,
NotificationType.ERROR
);
}
);
this._manualAnnotationService.acceptSuggestion(annotation).subscribe(() => {});
}
});
return ref;
}
public suggestRemoveAnnotation(
public rejectSuggestion(
$event: MouseEvent,
annotation: AnnotationWrapper,
projectId: string,
fileId: string
annotation: AnnotationWrapper
): MatDialogRef<ConfirmationDialogComponent> {
$event.stopPropagation();
@ -154,29 +128,7 @@ export class DialogService {
ref.afterClosed().subscribe((result) => {
if (result) {
this._manualRedactionControllerService
.undo(projectId, fileId, annotation.uuid)
.subscribe(
(ok) => {
this._notificationService.showToastNotification(
this._translateService.instant(
'manual-redaction.remove-annotation.success.label'
),
null,
NotificationType.SUCCESS
);
},
(err) => {
this._notificationService.showToastNotification(
this._translateService.instant(
'manual-redaction.remove-annotation.failed.label',
err
),
null,
NotificationType.ERROR
);
}
);
this._manualAnnotationService.rejectSuggestion(annotation).subscribe(() => {});
}
});

View File

@ -8,7 +8,10 @@
</div>
{{ manualRedactionEntryWrapper.manualRedactionEntry.value }}
<div class="red-input-group">
<div
class="red-input-group"
[class.hidden]="this.manualRedactionEntryWrapper.type === 'DICTIONARY'"
>
<label translate="manual-redaction.dialog.content.reason.label"></label>
<input formControlName="reason" name="reason" type="text" rows="2" />
</div>
@ -29,15 +32,6 @@
</mat-option>
</mat-select>
</div>
<div
class="red-input-group"
[class.hidden]="this.manualRedactionEntryWrapper.type === 'HINT'"
>
<mat-checkbox color="primary" formControlName="addToDictionary">{{
'manual-redaction.dialog.content.dictionary.add.label' | translate
}}</mat-checkbox>
</div>
</div>
<div class="dialog-actions">

View File

@ -0,0 +1,102 @@
import { Component, Inject, OnInit } from '@angular/core';
import { FormBuilder, FormGroup, Validators } from '@angular/forms';
import { AppStateService } from '../../state/app-state.service';
import { MAT_DIALOG_DATA, MatDialogRef } from '@angular/material/dialog';
import { AddRedactionRequest, TypeValue } from '@redaction/red-ui-http';
import { NotificationService } from '../../notification/notification.service';
import { TranslateService } from '@ngx-translate/core';
import { UserService } from '../../user/user.service';
import { ManualRedactionEntryWrapper } from '../../screens/file/model/manual-redaction-entry.wrapper';
import { ManualAnnotationService } from '../../screens/file/service/manual-annotation.service';
@Component({
selector: 'redaction-manual-annotation-dialog',
templateUrl: './manual-annotation-dialog.component.html',
styleUrls: ['./manual-annotation-dialog.component.scss']
})
export class ManualAnnotationDialogComponent implements OnInit {
dictionaryOptions: TypeValue[] = [];
redactionForm: FormGroup;
isDocumentAdmin: boolean;
constructor(
private readonly _appStateService: AppStateService,
private readonly _userService: UserService,
private readonly _formBuilder: FormBuilder,
private readonly _notificationService: NotificationService,
private readonly _translateService: TranslateService,
private readonly _manualAnnotationService: ManualAnnotationService,
public dialogRef: MatDialogRef<ManualAnnotationDialogComponent>,
@Inject(MAT_DIALOG_DATA) public manualRedactionEntryWrapper: ManualRedactionEntryWrapper
) {}
async ngOnInit() {
this.isDocumentAdmin = this._appStateService.isActiveProjectOwner;
const commentField = this.isDocumentAdmin ? [null] : [null, Validators.required];
this.redactionForm = this._formBuilder.group({
reason:
this.manualRedactionEntryWrapper.type === 'DICTIONARY'
? null
: [null, Validators.required],
dictionary: [null, Validators.required],
comment: commentField
});
for (const key of Object.keys(this._appStateService.dictionaryData)) {
const dictionaryData = this._appStateService.dictionaryData[key];
if (!dictionaryData.virtual) {
if (this.manualRedactionEntryWrapper.type === 'DICTIONARY') {
this.dictionaryOptions.push(dictionaryData);
}
if (!dictionaryData.hint && this.manualRedactionEntryWrapper.type === 'REDACTION') {
this.dictionaryOptions.push(dictionaryData);
}
}
}
}
handleAddRedaction() {
this._enhanceManualRedaction(this.manualRedactionEntryWrapper.manualRedactionEntry);
if (this.manualRedactionEntryWrapper.type === 'DICTIONARY') {
this._manualAnnotationService
.makeDictionaryEntry(this.manualRedactionEntryWrapper.manualRedactionEntry)
.subscribe(
(response) => {
this.dialogRef.close(this.manualRedactionEntryWrapper);
},
() => {
this.dialogRef.close();
}
);
} else {
this._manualAnnotationService
.makeRedaction(this.manualRedactionEntryWrapper.manualRedactionEntry)
.subscribe(
(response) => {
this.dialogRef.close(this.manualRedactionEntryWrapper);
},
() => {
this.dialogRef.close();
}
);
}
}
get title() {
return this._manualAnnotationService.getTitle(this.manualRedactionEntryWrapper.type);
}
private _enhanceManualRedaction(addRedactionRequest: AddRedactionRequest) {
addRedactionRequest.type = this.redactionForm.get('dictionary').value;
addRedactionRequest.addToDictionary =
this.manualRedactionEntryWrapper.type === 'DICTIONARY';
addRedactionRequest.reason = this.redactionForm.get('reason').value;
// todo fix this in backend
if (!addRedactionRequest.reason) {
addRedactionRequest.reason = 'ADD_TO_DICTIONARY_REQUEST';
}
const commentValue = this.redactionForm.get('comment').value;
addRedactionRequest.comment = commentValue ? { text: commentValue } : null;
}
}

View File

@ -1,161 +0,0 @@
import { Component, Inject, OnInit } from '@angular/core';
import { FormBuilder, FormGroup, Validators } from '@angular/forms';
import { AppStateService } from '../../state/app-state.service';
import { MAT_DIALOG_DATA, MatDialogRef } from '@angular/material/dialog';
import {
AddRedactionRequest,
DictionaryControllerService,
ManualRedactionControllerService,
TypeValue
} from '@redaction/red-ui-http';
import { NotificationService, NotificationType } from '../../notification/notification.service';
import { TranslateService } from '@ngx-translate/core';
import { UserService } from '../../user/user.service';
import { ManualRedactionEntryWrapper } from '../../screens/file/model/manual-redaction-entry.wrapper';
@Component({
selector: 'redaction-manual-redaction-dialog',
templateUrl: './manual-redaction-dialog.component.html',
styleUrls: ['./manual-redaction-dialog.component.scss']
})
export class ManualRedactionDialogComponent implements OnInit {
dictionaryOptions: TypeValue[] = [];
redactionForm: FormGroup;
isDocumentAdmin: boolean;
constructor(
private readonly _appStateService: AppStateService,
private readonly _userService: UserService,
private readonly _formBuilder: FormBuilder,
private readonly _notificationService: NotificationService,
private readonly _translateService: TranslateService,
private readonly _manualRedactionControllerService: ManualRedactionControllerService,
private readonly _dictionaryControllerService: DictionaryControllerService,
public dialogRef: MatDialogRef<ManualRedactionDialogComponent>,
@Inject(MAT_DIALOG_DATA) public manualRedactionEntryWrapper: ManualRedactionEntryWrapper
) {}
async ngOnInit() {
this.isDocumentAdmin = this._appStateService.isActiveProjectOwner;
const commentField = this.isDocumentAdmin ? [null] : [null, Validators.required];
this.redactionForm = this._formBuilder.group({
addToDictionary: [false],
reason: [null, Validators.required],
dictionary: [null, Validators.required],
comment: commentField
});
for (const key of Object.keys(this._appStateService.dictionaryData)) {
const dictionaryData = this._appStateService.dictionaryData[key];
if (!dictionaryData.virtual) {
if (dictionaryData.hint && this.manualRedactionEntryWrapper.type === 'HINT') {
this.dictionaryOptions.push(dictionaryData);
}
if (!dictionaryData.hint && this.manualRedactionEntryWrapper.type === 'REDACTION') {
this.dictionaryOptions.push(dictionaryData);
}
}
}
}
handleAddRedaction() {
if (this.isDocumentAdmin) {
this.addManualRedaction();
} else {
this.suggestManualRedaction();
}
}
suggestManualRedaction() {
const mre = Object.assign({}, this.manualRedactionEntryWrapper.manualRedactionEntry);
this._enhanceManualRedaction(mre);
this._manualRedactionControllerService
.requestAddRedaction(
mre,
this._appStateService.activeProject.project.projectId,
this._appStateService.activeFile.fileId
)
.subscribe(
(ok) => {
this._notificationService.showToastNotification(
this._translateService.instant(
'manual-redaction.dialog.add-redaction.success.label'
),
null,
NotificationType.SUCCESS
);
this.dialogRef.close({ mode: 'suggestion', request: mre });
},
(err) => {
this._notificationService.showToastNotification(
this._translateService.instant(
'manual-redaction.dialog.add-redaction.failed.label',
err
),
null,
NotificationType.ERROR
);
}
);
}
addManualRedaction() {
const mre = Object.assign({}, this.manualRedactionEntryWrapper.manualRedactionEntry);
this._enhanceManualRedaction(mre);
this._manualRedactionControllerService
.addRedaction(
mre,
this._appStateService.activeProject.project.projectId,
this._appStateService.activeFile.fileId
)
.subscribe(
(ok) => {
this._notificationService.showToastNotification(
this._translateService.instant(
'manual-redaction.dialog.add-redaction.success.label'
),
null,
NotificationType.SUCCESS
);
this.dialogRef.close({ mode: 'redaction', request: mre });
},
(err) => {
this._notificationService.showToastNotification(
this._translateService.instant(
'manual-redaction.dialog.add-redaction.failed.label',
err
),
null,
NotificationType.ERROR
);
}
);
}
get title() {
if (this.isDocumentAdmin) {
if (this.manualRedactionEntryWrapper.type === 'HINT') {
return 'manual-redaction.dialog.header.hint.label';
} else {
return 'manual-redaction.dialog.header.redaction.label';
}
} else {
if (this.manualRedactionEntryWrapper.type === 'HINT') {
return 'manual-redaction.dialog.header.request-hint.label';
} else {
return 'manual-redaction.dialog.header.request-redaction.label';
}
}
}
private _enhanceManualRedaction(addRedactionRequest: AddRedactionRequest) {
addRedactionRequest.type = this.redactionForm.get('dictionary').value;
addRedactionRequest.addToDictionary =
this.manualRedactionEntryWrapper.type === 'HINT'
? true
: this.redactionForm.get('addToDictionary').value;
addRedactionRequest.reason = this.redactionForm.get('reason').value;
const commentValue = this.redactionForm.get('comment').value;
addRedactionRequest.comment = commentValue ? { text: commentValue } : null;
}
}

View File

@ -267,14 +267,13 @@
>
<button
mat-icon-button
(click)="acceptSuggestionAnnotation($event, annotation)"
class="confirm"
(click)="acceptSuggestion($event, annotation)"
>
<mat-icon svgIcon="red:check-alt"></mat-icon>
</button>
<button
mat-icon-button
(click)="suggestRemoveAnnotation($event, annotation)"
(click)="rejectSuggestion($event, annotation)"
>
<mat-icon svgIcon="red:trash"></mat-icon>
</button>

View File

@ -26,6 +26,7 @@ import { MatDialogRef, MatDialogState } from '@angular/material/dialog';
import { ManualRedactionEntryWrapper } from '../model/manual-redaction-entry.wrapper';
import { hexToRgb } from '../../../utils/functions';
import { AnnotationWrapper } from '../model/annotation.wrapper';
import { ManualAnnotationService } from '../service/manual-annotation.service';
@Component({
selector: 'redaction-file-preview-screen',
@ -60,6 +61,7 @@ export class FilePreviewScreenComponent implements OnInit {
private readonly _activatedRoute: ActivatedRoute,
private readonly _dialogService: DialogService,
private readonly _router: Router,
private readonly _manualAnnotationService: ManualAnnotationService,
private readonly _fileDownloadService: FileDownloadService,
private readonly _reanalysisControllerService: ReanalysisControllerService,
private readonly _filtersService: FiltersService,
@ -193,11 +195,12 @@ export class FilePreviewScreenComponent implements OnInit {
this.ngZone.run(() => {
this._dialogRef = this._dialogService.openManualRedactionDialog(
$event,
(response: any) => {
const manualRedactionEntry: ManualRedactionEntry = response.request;
(response: ManualRedactionEntryWrapper) => {
const manualRedactionEntry: ManualRedactionEntry =
response.manualRedactionEntry;
const annotManager = this.activeViewer.annotManager;
const originalQuads = manualRedactionEntry.quads;
const originalQuads = $event.quads;
for (const key of Object.keys(originalQuads)) {
const pageNumber = parseInt(key, 10);
const highlight = new this.activeViewer.Annotations.TextHighlightAnnotation();
@ -205,7 +208,7 @@ export class FilePreviewScreenComponent implements OnInit {
highlight.StrokeColor = this._getColor(manualRedactionEntry);
highlight.setContents(manualRedactionEntry.reason);
highlight.Quads = originalQuads[key];
highlight.Id = this._computeId($event.type, manualRedactionEntry);
highlight.Id = this._computeId(response.type, manualRedactionEntry);
annotManager.addAnnotation(highlight, true);
annotManager.redrawAnnotation(highlight);
}
@ -259,25 +262,15 @@ export class FilePreviewScreenComponent implements OnInit {
}
}
public acceptSuggestionAnnotation($event: MouseEvent, annotation: AnnotationWrapper) {
public acceptSuggestion($event: MouseEvent, annotation: AnnotationWrapper) {
this.ngZone.run(() => {
this._dialogRef = this._dialogService.acceptSuggestionAnnotation(
$event,
annotation,
this.projectId,
this.fileId
);
this._dialogRef = this._dialogService.acceptSuggestion($event, annotation);
});
}
public suggestRemoveAnnotation($event: MouseEvent, annotation: AnnotationWrapper) {
public rejectSuggestion($event: MouseEvent, annotation: AnnotationWrapper) {
this.ngZone.run(() => {
this._dialogRef = this._dialogService.suggestRemoveAnnotation(
$event,
annotation,
this.projectId,
this.fileId
);
this._dialogRef = this._dialogService.rejectSuggestion($event, annotation);
});
}
@ -477,9 +470,21 @@ export class FilePreviewScreenComponent implements OnInit {
this._changeDetectorRef.detectChanges();
}
private _computeId(type: 'HINT' | 'REDACTION', request: ManualRedactionEntry) {
const prefix = this.appStateService.isActiveProjectOwner ? type.toLowerCase() : 'request';
return prefix + ':' + request.type + ':' + new Date().getTime();
private _computeId(type: 'DICTIONARY' | 'REDACTION', request: ManualRedactionEntry) {
// if owner or not set the request prefix in the id
const prefix = this.appStateService.isActiveProjectOwner ? '' : 'request:add:';
if (prefix.length > 0) {
return prefix + request.type + ':' + new Date().getTime();
} else {
const dictionaryType: 'hint' | 'redaction' =
type === 'REDACTION'
? 'redaction'
: this.appStateService.getDictionaryTypeValue(request.type).hint
? 'hint'
: 'redaction';
return dictionaryType + ':' + request.type + ':' + new Date().getTime();
}
}
private _getColor(manualRedactionEntry: ManualRedactionEntry) {

View File

@ -1,10 +1,9 @@
import { ManualRedactionEntry } from '@redaction/red-ui-http';
export class ManualRedactionEntryWrapper {
public mode: 'REQUEST' | 'ACTUAL';
constructor(
public readonly quads: any,
public readonly manualRedactionEntry: ManualRedactionEntry,
public readonly type: 'HINT' | 'REDACTION'
public readonly type: 'DICTIONARY' | 'REDACTION'
) {}
}

View File

@ -12,7 +12,12 @@ import {
ViewChild
} from '@angular/core';
import { AppConfigKey, AppConfigService } from '../../../app-config/app-config.service';
import { FileStatus, ManualRedactionEntry, Rectangle } from '@redaction/red-ui-http';
import {
FileStatus,
ManualRedactionEntry,
ManualRedactions,
Rectangle
} from '@redaction/red-ui-http';
import WebViewer, { Annotations, WebViewerInstance } from '@pdftron/webviewer';
import { TranslateService } from '@ngx-translate/core';
import { FileDownloadService } from '../service/file-download.service';
@ -22,6 +27,7 @@ import { ManualRedactionEntryWrapper } from '../model/manual-redaction-entry.wra
import { AppStateService } from '../../../state/app-state.service';
import { AnnotationWrapper } from '../model/annotation.wrapper';
import { AnnotationUtils } from '../../../utils/annotation-utils';
import { ManualAnnotationService } from '../service/manual-annotation.service';
export interface ViewerState {
displayMode?: any;
@ -58,12 +64,14 @@ export class PdfViewerComponent implements OnInit, AfterViewInit, OnChanges {
@ViewChild('viewer', { static: true }) viewer: ElementRef;
instance: WebViewerInstance;
private _manualAnnotations: ManualRedactions;
constructor(
private readonly _appStateService: AppStateService,
private readonly _translateService: TranslateService,
private readonly _fileDownloadService: FileDownloadService,
private readonly _appConfigService: AppConfigService,
private readonly _manualAnnotationService: ManualAnnotationService,
private readonly _ngZone: NgZone
) {}
@ -87,6 +95,9 @@ export class PdfViewerComponent implements OnInit, AfterViewInit, OnChanges {
200
);
});
this._manualAnnotationService.getManualAnnotations().subscribe((manualAnnotation) => {
this._manualAnnotations = manualAnnotation;
});
}
ngOnChanges(changes: SimpleChanges): void {
@ -195,14 +206,18 @@ export class PdfViewerComponent implements OnInit, AfterViewInit, OnChanges {
if (this._appStateService.isActiveFileDocumentReviewer) {
this.instance.textPopup.add(<any>{
type: 'actionButton',
img: '/assets/icons/general/add-hint.svg',
img: '/assets/icons/general/add-dictionary.svg',
title: this._translateService.instant(
'pdf-viewer.text-popup.actions.suggestion-hint.label'
this._manualAnnotationService.getTitle('DICTIONARY')
),
onClick: () => {
const mre = this._getManualRedactionEntry();
this.manualAnnotationRequested.emit(
new ManualRedactionEntryWrapper(mre, 'HINT')
new ManualRedactionEntryWrapper(
this.instance.docViewer.getSelectedTextQuads(),
mre,
'DICTIONARY'
)
);
}
});
@ -210,12 +225,16 @@ export class PdfViewerComponent implements OnInit, AfterViewInit, OnChanges {
type: 'actionButton',
img: '/assets/icons/general/add-redaction.svg',
title: this._translateService.instant(
'pdf-viewer.text-popup.actions.suggestion-redaction.label'
this._manualAnnotationService.getTitle('REDACTION')
),
onClick: () => {
const mre = this._getManualRedactionEntry();
this.manualAnnotationRequested.emit(
new ManualRedactionEntryWrapper(mre, 'REDACTION')
new ManualRedactionEntryWrapper(
this.instance.docViewer.getSelectedTextQuads(),
mre,
'REDACTION'
)
);
}
});
@ -231,7 +250,6 @@ export class PdfViewerComponent implements OnInit, AfterViewInit, OnChanges {
entry.positions.push(this.toPosition(parseInt(key, 10), quad));
}
}
entry.quads = selectedQuads;
entry.value = text;
return entry;
}

View File

@ -0,0 +1,185 @@
import { Injectable } from '@angular/core';
import { AppStateService } from '../../../state/app-state.service';
import {
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';
@Injectable({
providedIn: 'root'
})
export class ManualAnnotationService {
constructor(
private readonly _appStateService: AppStateService,
private readonly _translateService: TranslateService,
private readonly _notificationService: NotificationService,
private readonly _manualRedactionControllerService: ManualRedactionControllerService,
private readonly _dictionaryControllerService: DictionaryControllerService
) {}
public makeDictionaryEntry(manualRedactionEntry: ManualRedactionEntry) {
if (this._appStateService.isActiveProjectOwner) {
return this._makeDictionaryEntry(manualRedactionEntry);
} else {
return this._makeDictionaryRequest(manualRedactionEntry);
}
}
public makeRedaction(manualRedactionEntry: ManualRedactionEntry) {
if (this._appStateService.isActiveProjectOwner) {
return this._makeRedaction(manualRedactionEntry);
} else {
return this._makeRedactionRequest(manualRedactionEntry);
}
}
public acceptSuggestion(annotationWrapper: AnnotationWrapper) {
// for only here - approve the request
return this._manualRedactionControllerService
.approveRequest(
this._appStateService.activeProjectId,
this._appStateService.activeFile.fileId,
annotationWrapper.uuid
)
.pipe(
tap(
() => {
// if approved - and not only here, make a dictionary entry
if (annotationWrapper.id.indexOf(':only_here:') < 0) {
this.getManualAnnotations().subscribe((annotations) => {
annotations.entriesToAdd.forEach((a) => {
// found the annotation
if (annotationWrapper.id.indexOf(a.id) >= 0) {
console.log(a);
this._makeDictionaryEntry(a).subscribe(() => {});
}
});
});
} else {
this._notify('manual-annotation.reject-request.success');
}
},
() => {
this._notify('manual-annotation.reject-request.error');
}
)
);
}
public rejectSuggestion(annotationWrapper: AnnotationWrapper) {
return this._manualRedactionControllerService
.undo(
this._appStateService.activeProjectId,
this._appStateService.activeFile.fileId,
annotationWrapper.uuid
)
.pipe(
tap(
() => {
this._notify('manual-annotation.reject-request.success');
},
() => {
this._notify('manual-annotation.reject-request.error');
}
)
);
}
public removeRedaction(annotationWrapper: AnnotationWrapper) {}
private _makeDictionaryRequest(manualRedactionEntry: ManualRedactionEntry) {
return this._makeRedactionRequest(manualRedactionEntry);
}
private _makeDictionaryEntry(manualRedactionEntry: ManualRedactionEntry) {
return this._dictionaryControllerService
.addEntry([manualRedactionEntry.value], manualRedactionEntry.type)
.pipe(
tap(
() => this._notify('manual-annotation.dictionary-add.success'),
() => {
this._notify(
'manual-annotation.dictionary-add.error',
NotificationType.ERROR
);
}
)
);
}
private _makeRedactionRequest(manualRedactionEntry: ManualRedactionEntry) {
return this._manualRedactionControllerService
.requestAddRedaction(
manualRedactionEntry,
this._appStateService.activeProject.project.projectId,
this._appStateService.activeFile.fileId
)
.pipe(
tap(
() => this._notify('manual-annotation.redaction-request.success'),
() => {
this._notify(
'manual-annotation.redaction-request.error',
NotificationType.ERROR
);
}
)
);
}
private _makeRedaction(manualRedactionEntry: ManualRedactionEntry) {
return this._manualRedactionControllerService
.addRedaction(
manualRedactionEntry,
this._appStateService.activeProject.project.projectId,
this._appStateService.activeFile.fileId
)
.pipe(
tap(
() => this._notify('manual-annotation.redaction-add.success'),
() => {
this._notify(
'manual-annotation.redaction-add.error',
NotificationType.ERROR
);
}
)
);
}
private _notify(key: string, type: NotificationType = NotificationType.SUCCESS) {
this._notificationService.showToastNotification(
this._translateService.instant(key),
null,
type
);
}
getTitle(type: 'DICTIONARY' | 'REDACTION') {
if (this._appStateService.isActiveProjectOwner) {
if (type === 'DICTIONARY') {
return 'manual-redaction.dialog.header.dictionary.label';
} else {
return 'manual-redaction.dialog.header.redaction.label';
}
} else {
if (type === 'DICTIONARY') {
return 'manual-redaction.dialog.header.request-dictionary.label';
} else {
return 'manual-redaction.dialog.header.request-redaction.label';
}
}
}
getManualAnnotations() {
return this._manualRedactionControllerService.getManualRedaction(
this._appStateService.activeProject.project.projectId,
this._appStateService.activeFile.fileId
);
}
}

View File

@ -35,14 +35,14 @@
},
"dialog": {
"header": {
"hint": {
"label": "Add Hint"
"dictionary": {
"label": "Add to dictionary"
},
"redaction": {
"label": "Add Redaction"
},
"request-hint": {
"label": "Request Hint"
"request-dictionary": {
"label": "Request add to dictionary"
},
"request-redaction": {
"label": "Request Redaction"

View File

@ -0,0 +1,68 @@
<?xml version="1.0" encoding="iso-8859-1"?>
<!-- Generator: Adobe Illustrator 19.0.0, SVG Export Plug-In . SVG Version: 6.00 Build 0) -->
<svg version="1.1" id="Capa_1" xmlns="http://www.w3.org/2000/svg" x="0px" y="0px"
viewBox="0 0 496 496" style="enable-background:new 0 0 496 496;" xml:space="preserve">
<g fill="rgb(136,142,149)">
<g>
<path d="M217.528,96h-14.4L183.44,32h-27.824l-19.688,64h-14.4v16h9.48l-9.128,29.648l15.288,4.704L147.744,112h43.568
l10.568,34.352l15.288-4.704L208.048,112h9.48V96z M152.664,96l14.776-48h4.176l14.768,48H152.664z"/>
</g>
</g>
<g fill="rgb(136,142,149)">
<g>
<path d="M294.672,88c6.656-5.864,10.856-14.456,10.856-24c0-17.648-14.352-32-32-32h-32v112h32c17.648,0,32-14.352,32-32
C305.528,102.456,301.328,93.864,294.672,88z M273.528,128h-16V96h16c8.824,0,16,7.176,16,16S282.352,128,273.528,128z
M273.528,80h-16V48h16c8.824,0,16,7.176,16,16S282.352,80,273.528,80z"/>
</g>
</g>
<g fill="rgb(136,142,149)">
<g>
<path d="M377.528,48V32c-30.88,0-56,25.12-56,56s25.12,56,56,56v-16c-22.056,0-40-17.944-40-40
C337.528,65.944,355.472,48,377.528,48z"/>
</g>
</g>
<g fill="rgb(136,142,149)">
<g>
<path d="M97.528,160v144h304V160H97.528z M385.528,288h-272V176h272V288z"/>
</g>
</g>
<g fill="rgb(136,142,149)">
<g>
<rect x="105.528" y="320" width="288" height="16"/>
</g>
</g>
<g fill="rgb(136,142,149)">
<g>
<rect x="105.528" y="352" width="288" height="16"/>
</g>
</g>
<g fill="rgb(136,142,149)">
<g>
<rect x="377.528" y="384" width="16" height="16"/>
</g>
</g>
<g fill="rgb(136,142,149)">
<g>
<rect x="105.528" y="384" width="256" height="16"/>
</g>
</g>
<g fill="rgb(136,142,149)">
<g>
<path d="M494.472,32h-60.944V0h-392c-22.056,0-40,17.944-40,40v416c0,22.056,17.944,40,40,40h368h24v-16h-24
c-13.232,0-24-10.768-24-24s10.768-24,24-24h16h8V112h60.944l-20-40L494.472,32z M377.736,480H41.528c-13.232,0-24-10.768-24-24
s10.768-24,24-24h336.208c-5.08,6.704-8.208,14.96-8.208,24C369.528,465.04,372.656,473.296,377.736,480z M417.528,416h-8h-328
v-32h-16v32h-24c-9,0-17.312,2.992-24,8.016V40c0-13.232,10.768-24,24-24h24v352h16V16h336V416z M468.584,96h-35.056V48h35.056
l-12,24L468.584,96z"/>
</g>
</g>
<g fill="rgb(136,142,149)">
<g>
<rect x="65.528" y="448" width="288" height="16"/>
</g>
</g>
<g fill="rgb(136,142,149)">
<g>
<rect x="33.528" y="448" width="16" height="16"/>
</g>
</g>
</svg>

After

Width:  |  Height:  |  Size: 2.4 KiB

View File

@ -9,22 +9,19 @@
* https://github.com/swagger-api/swagger-codegen.git
* Do not edit the class manually.
*/
import { Comment } from './comment';
import { Rectangle } from './rectangle';
export interface ManualRedactionEntry {
addToDictionary?: boolean;
comments?: Array<Comment>;
id?: string;
legalBasis?: string;
positions?: Array<Rectangle>;
reason?: string;
status?: ManualRedactionEntry.StatusEnum;
type?: string;
user?: string;
value?: string;
[key: string]: any;
}
export namespace ManualRedactionEntry {
export type StatusEnum = 'REQUESTED' | 'APPROVED' | 'DECLINED';
export const StatusEnum = {

View File

@ -9,10 +9,12 @@
* https://github.com/swagger-api/swagger-codegen.git
* Do not edit the class manually.
*/
import { Comment } from './comment';
import { IdRemoval } from './idRemoval';
import { ManualRedactionEntry } from './manualRedactionEntry';
export interface ManualRedactions {
comments?: { [key: string]: Array<Comment> };
entriesToAdd?: Array<ManualRedactionEntry>;
idsToRemove?: Array<IdRemoval>;
}