linked redaction file screen to backend colors and dictionary data

This commit is contained in:
Timo Bejan 2020-10-27 23:38:16 +02:00
parent b4888b49ce
commit f1f3dda4e3
15 changed files with 135 additions and 76 deletions

View File

@ -52,5 +52,11 @@
"secure": false,
"changeOrigin": true,
"logLevel": "debug"
},
"/color": {
"target": "https://timo-redaction-dev.iqser.cloud/",
"secure": false,
"changeOrigin": true,
"logLevel": "debug"
}
}

View File

@ -1,3 +1,3 @@
<div [ngClass]="type" class="icon">
<span>{{ type[0] }}</span>
<div class="icon" [class.hint]="typeValue.hint" [style.background-color]="typeValue.hexColor">
<span>{{ typeValue.type[0] }}</span>
</div>

View File

@ -40,33 +40,10 @@
}
.hint,
.comment,
.ignore {
border-radius: 50%;
}
.hint,
.redaction,
.comment {
background-color: $grey-1;
}
.request {
background-color: $blue-1;
}
.ignore {
background-color: $grey-5;
}
.hint_only {
background-color: $orange-1;
}
.vertebrate {
background-color: $green-1;
}
.names {
background-color: $yellow-2;
}

View File

@ -1,4 +1,5 @@
import { Component, Input, OnInit } from '@angular/core';
import { TypeValue } from '@redaction/red-ui-http';
@Component({
selector: 'redaction-annotation-icon',
@ -6,7 +7,7 @@ import { Component, Input, OnInit } from '@angular/core';
styleUrls: ['./annotation-icon.component.scss']
})
export class AnnotationIconComponent implements OnInit {
@Input() public type: 'hint' | 'redaction' | 'suggestion' | 'ignore' | 'comment' | 'request';
@Input() typeValue: TypeValue;
constructor() {}

View File

@ -38,9 +38,8 @@ export class ManualRedactionDialogComponent implements OnInit {
async ngOnInit() {
this.isDocumentAdmin =
(this._appStateService.isActiveProjectOwner ||
this._appStateService.isActiveFileDocumentReviewer) &&
this._userService.user.isManager;
this._appStateService.isActiveProjectOwner && this._userService.user.isManager;
this.isDocumentAdmin = false;
const commentField = this.isDocumentAdmin ? [null] : [null, Validators.required];
this.redactionForm = this._formBuilder.group({
addToDictionary: [false],

View File

@ -145,8 +145,9 @@
color="primary"
>
<redaction-annotation-icon
[type]="key"
[typeValue]="appStateService.getDictionaryTypeValue(key)"
></redaction-annotation-icon>
{{ 'file-preview.filter-menu.' + key + '.label' | translate }}
</mat-checkbox>
</div>
@ -162,15 +163,11 @@
color="primary"
>
<redaction-annotation-icon
[type]="key + ' ' + subkey"
[typeValue]="
appStateService.getDictionaryTypeValue(subkey)
"
></redaction-annotation-icon>
{{
'file-preview.filter-menu.' +
key +
'.' +
subkey +
'.label' | translate
}}
{{ appStateService.getDictionaryLabel(subkey) }}
</mat-checkbox>
</div>
</div>
@ -223,7 +220,11 @@
(click)="selectAnnotation(annotation)"
>
<redaction-annotation-icon
[type]="getType(annotation) + ' ' + getDictionary(annotation)"
[typeValue]="
appStateService.getDictionaryTypeValue(
getDictionary(annotation)
)
"
></redaction-annotation-icon>
<div class="flex-1">
<div>

View File

@ -8,7 +8,12 @@ import {
ViewChild
} from '@angular/core';
import { ActivatedRoute, Router } from '@angular/router';
import { ManualRedactionEntry, ReanalysisControllerService } from '@redaction/red-ui-http';
import {
DictionaryControllerService,
ManualRedactionEntry,
ReanalysisControllerService,
TypeValue
} from '@redaction/red-ui-http';
import { AppStateService } from '../../../state/app-state.service';
import { Annotations, WebViewerInstance } from '@pdftron/webviewer';
import { PdfViewerComponent } from '../pdf-viewer/pdf-viewer.component';
@ -56,6 +61,7 @@ export class FilePreviewScreenComponent implements OnInit {
private readonly _activatedRoute: ActivatedRoute,
private readonly _dialogService: DialogService,
private readonly _router: Router,
private readonly _dictionaryControllerService: DictionaryControllerService,
private readonly _userService: UserService,
private readonly _fileDownloadService: FileDownloadService,
private readonly _reanalysisControllerService: ReanalysisControllerService,
@ -67,7 +73,6 @@ export class FilePreviewScreenComponent implements OnInit {
this.fileId = params.fileId;
this.appStateService.activateFile(this.projectId, this.fileId);
});
this.filters = _filtersService.filters;
}
public get user() {
@ -91,7 +96,7 @@ export class FilePreviewScreenComponent implements OnInit {
}
public ngOnInit(): void {
// PDFTRON cache fix
this.filters = this._filtersService.getFilters(this.appStateService.dictionaryData);
this._reloadFiles();
this.appStateService.fileStatusChanged.subscribe((fileStatus) => {
if (fileStatus.fileId === this.fileId) {
@ -273,7 +278,7 @@ export class FilePreviewScreenComponent implements OnInit {
});
}
public setAllFilters(filter: AnnotationFilters, value: boolean, rootKey?: string) {
public setAllFilters(filter: string, value: boolean, rootKey?: string) {
if (rootKey) {
this.filters[rootKey] = value;
} else {

View File

@ -1,5 +1,6 @@
import { Injectable } from '@angular/core';
import { AnnotationFilters } from '../../../utils/types';
import { TypeValue } from '@redaction/red-ui-http';
@Injectable({
providedIn: 'root'
@ -8,18 +9,23 @@ export class FiltersService {
constructor() {}
private _filters: AnnotationFilters = {
hint: {
hint_only: false,
vertebrate: false,
names: false
},
redaction: false,
comment: false,
suggestion: false,
hint: {},
redaction: {},
request: false,
ignore: false
};
public get filters(): AnnotationFilters {
return JSON.parse(JSON.stringify(this._filters));
public getFilters(dictionaryData: { [key: string]: TypeValue }): AnnotationFilters {
const filtersCopy = JSON.parse(JSON.stringify(this._filters));
for (let key of Object.keys(dictionaryData)) {
const typeValue = dictionaryData[key];
if (typeValue.hint === true) {
filtersCopy.hint[key] = false;
}
if (typeValue.hint === false) {
filtersCopy.redaction[key] = false;
}
}
return filtersCopy;
}
}

View File

@ -15,6 +15,7 @@ export class AppStateGuard implements CanActivate {
async canActivate(route: ActivatedRouteSnapshot, state: RouterStateSnapshot): Promise<boolean> {
await this._userService.loadAllUsersIfNecessary();
await this._appStateService.loadAllProjectsIfNecessary();
await this._appStateService.loadDictionaryDataIfNecessary();
return true;
}

View File

@ -1,19 +1,22 @@
import { EventEmitter, Injectable } from '@angular/core';
import {
DictionaryControllerService,
FileStatus,
FileUploadControllerService,
Project,
ProjectControllerService,
ReanalysisControllerService,
StatusControllerService
StatusControllerService,
TypeValue
} from '@redaction/red-ui-http';
import { NotificationService, NotificationType } from '../notification/notification.service';
import { TranslateService } from '@ngx-translate/core';
import { Router } from '@angular/router';
import { UserService } from '../user/user.service';
import { interval } from 'rxjs';
import { forkJoin, interval } from 'rxjs';
import { tap } from 'rxjs/operators';
import { download } from '../utils/file-download-utils';
import { humanize } from '../utils/functions';
export interface AppState {
projects: ProjectWrapper[];
@ -37,6 +40,7 @@ export class ProjectWrapper {
})
export class AppStateService {
private _appState: AppState;
private _dictionaryData: { [key: string]: TypeValue } = null;
public fileStatusChanged = new EventEmitter<FileStatus>();
@ -48,6 +52,7 @@ export class AppStateService {
private readonly _notificationService: NotificationService,
private readonly _reanalysisControllerService: ReanalysisControllerService,
private readonly _translateService: TranslateService,
private readonly _dictionaryControllerService: DictionaryControllerService,
private readonly _statusControllerService: StatusControllerService
) {
this._appState = {
@ -90,6 +95,19 @@ export class AppStateService {
);
}
get dictionaryData() {
return this._dictionaryData;
}
getDictionaryColor(type: string) {
const color = this._dictionaryData[type].hexColor;
return color ? color : this._dictionaryData['default'].hexColor;
}
getDictionaryLabel(type: string) {
return this._dictionaryData[type]['label'];
}
get isActiveFileDocumentReviewer() {
return this._appState.activeFile?.currentReviewer === this._userService.userId;
}
@ -331,4 +349,53 @@ export class AppStateService {
download(data, 'redaction-report-' + file.filename + '.docx');
});
}
async loadDictionaryDataIfNecessary() {
if (!this._dictionaryData) {
this._dictionaryData = {};
const typeObs = this._dictionaryControllerService.getAllTypes().pipe(
tap((typesResponse) => {
for (let type of typesResponse.types) {
this._dictionaryData[type.type] = type;
}
})
);
const colorsObs = this._dictionaryControllerService.getColors().pipe(
tap((colors) => {
this._dictionaryData['request'] = {
hexColor: colors.requestAdd,
type: 'request'
};
this._dictionaryData['ignore'] = {
hexColor: colors.notRedacted,
type: 'ignore'
};
this._dictionaryData['default'] = {
hexColor: colors.defaultColor,
type: 'default'
};
this._dictionaryData['add'] = { hexColor: colors.requestAdd, type: 'add' };
this._dictionaryData['remove'] = {
hexColor: colors.requestRemove,
type: 'remove'
};
})
);
const result = await forkJoin([typeObs, colorsObs]).toPromise();
this._dictionaryData['hint'] = { hexColor: '#283241', type: 'hint' };
this._dictionaryData['redaction'] = { hexColor: '#283241', type: 'redaction' };
for (let key of Object.keys(this._dictionaryData)) {
this._dictionaryData[key]['label'] = humanize(key);
}
} else {
return this._dictionaryData;
}
}
getDictionaryTypeValue(key: string) {
const data = this._dictionaryData[key];
return data ? data : this._dictionaryData['default'];
}
}

View File

@ -72,8 +72,7 @@ export class AnnotationUtils {
annotations: [],
hint: 0,
redaction: 0,
comment: 0,
suggestion: 0,
request: 0,
ignore: 0
};
}

View File

@ -4,3 +4,11 @@ export function groupBy(xs: any[], key: string) {
return rv;
}, {});
}
export function humanize(str: string) {
let frags = str.split(/[ \-_]+/);
for (let i = 0; i < frags.length; i++) {
frags[i] = frags[i].charAt(0).toUpperCase() + frags[i].slice(1);
}
return frags.join(' ');
}

View File

@ -2,8 +2,6 @@ import { FileStatus } from '@redaction/red-ui-http';
export type Color = FileStatus.StatusEnum | ProjectStatus.StatusEnum;
export type AnnotationType = 'hint' | 'redaction' | 'suggestion' | 'comment' | 'ignore';
export class SortingOption {
label: string;
order: string;
@ -11,5 +9,5 @@ export class SortingOption {
}
export class AnnotationFilters {
[key: AnnotationType]: boolean;
[key: string]: boolean | {};
}

View File

@ -442,26 +442,14 @@
"filter-types": {
"label": "Filter types"
},
"hint": {
"label": "Hint annotation",
"hint_only": {
"label": "Hint only"
},
"vertebrate": {
"label": "Vertebrate"
},
"names": {
"label": "Names"
}
},
"redaction": {
"label": "Redaction"
},
"comment": {
"label": "Comment annotation"
"hint": {
"label": "Hint"
},
"suggestion": {
"label": "Suggested redaction"
"request": {
"label": "Redaction Request"
},
"ignore": {
"label": "Ignored redaction"

View File

@ -17,6 +17,9 @@ server {
location /project {
proxy_pass $API_URL;
}
location /color {
proxy_pass $API_URL;
}
location /user {
proxy_pass $API_URL;
}