diff --git a/apps/red-ui/proxy.conf.json b/apps/red-ui/proxy.conf.json
index 53049ec45..bc4a43128 100644
--- a/apps/red-ui/proxy.conf.json
+++ b/apps/red-ui/proxy.conf.json
@@ -17,6 +17,12 @@
"changeOrigin": true,
"logLevel": "debug"
},
+ "/redactionLog": {
+ "target": "https://timo-redaction-dev.iqser.cloud/",
+ "secure": false,
+ "changeOrigin": true,
+ "logLevel": "debug"
+ },
"/user": {
"target": "https://timo-redaction-dev.iqser.cloud/",
"secure": false,
diff --git a/apps/red-ui/src/app/screens/file/file-preview-screen/file-preview-screen.component.html b/apps/red-ui/src/app/screens/file/file-preview-screen/file-preview-screen.component.html
index 797b0110b..502e773e0 100644
--- a/apps/red-ui/src/app/screens/file/file-preview-screen/file-preview-screen.component.html
+++ b/apps/red-ui/src/app/screens/file/file-preview-screen/file-preview-screen.component.html
@@ -74,14 +74,13 @@
-
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
diff --git a/apps/red-ui/src/app/screens/file/file-preview-screen/file-preview-screen.component.ts b/apps/red-ui/src/app/screens/file/file-preview-screen/file-preview-screen.component.ts
index b5554d63a..aa9f42429 100644
--- a/apps/red-ui/src/app/screens/file/file-preview-screen/file-preview-screen.component.ts
+++ b/apps/red-ui/src/app/screens/file/file-preview-screen/file-preview-screen.component.ts
@@ -16,7 +16,6 @@ import { AnnotationUtils } from '../../../utils/annotation-utils';
import { UserService } from '../../../user/user.service';
import { debounce } from '../../../utils/debounce';
import scrollIntoView from 'scroll-into-view-if-needed';
-import { AnnotationFilters } from '../../../utils/types';
import { FiltersService } from '../service/filters.service';
import { FileDownloadService } from '../service/file-download.service';
import { saveAs } from 'file-saver';
@@ -28,6 +27,8 @@ import { hexToRgb } from '../../../utils/functions';
import { AnnotationWrapper } from '../model/annotation.wrapper';
import { ManualAnnotationService } from '../service/manual-annotation.service';
import { ManualAnnotationResponse } from '../model/manual-annotation-response';
+import { FileDataModel } from '../model/file-data.model';
+import { AnnotationFilter } from '../../../utils/types';
@Component({
selector: 'redaction-file-preview-screen',
@@ -44,14 +45,12 @@ export class FilePreviewScreenComponent implements OnInit {
@ViewChild('annotations') private _annotationsElement: ElementRef;
@ViewChild('quickNavigation') private _quickNavigationElement: ElementRef;
- public annotatedFileData: Blob;
- public redactedFileData: Blob;
+ public fileData: FileDataModel;
public fileId: string;
public annotations: AnnotationWrapper[] = [];
public displayedAnnotations: { [key: number]: { annotations: AnnotationWrapper[] } } = {};
public selectedAnnotation: AnnotationWrapper;
- public filters: AnnotationFilters;
- public expandedFilters: AnnotationFilters = { hint: false };
+ public filters: AnnotationFilter[];
public pagesPanelActive = true;
public viewReady = false;
@@ -97,7 +96,7 @@ export class FilePreviewScreenComponent implements OnInit {
public ngOnInit(): void {
this.filters = this._filtersService.getFilters(this.appStateService.dictionaryData);
- this._reloadFiles();
+ this._loadFileData();
this.appStateService.fileStatusChanged.subscribe((fileStatus) => {
if (fileStatus.fileId === this.fileId) {
// no more automatic reloads
@@ -106,17 +105,20 @@ export class FilePreviewScreenComponent implements OnInit {
});
}
- private _reloadFiles() {
- this._fileDownloadService
- .loadFile('ANNOTATED', this.fileId, (data) => {
- this.annotatedFileData = data;
- })
- .subscribe(() => {});
- this._fileDownloadService
- .loadFile('REDACTED', this.fileId, (data) => {
- this.redactedFileData = data;
- })
- .subscribe(() => {});
+ private _loadFileData() {
+ this._fileDownloadService.loadFileData(this.fileId).subscribe((fileDataModel) => {
+ this.fileData = fileDataModel;
+ this.annotations = fileDataModel.redactionLog.redactionLogEntry.map(
+ (rde) => new AnnotationWrapper(rde, null)
+ );
+ this.filters = this._filtersService.getFilters(
+ this.appStateService.dictionaryData,
+ this.annotations
+ );
+ this.applyFilters();
+ this._changeDetectorRef.detectChanges();
+ console.log(this.annotations);
+ });
}
public openFileDetailsDialog($event: MouseEvent) {
@@ -166,8 +168,8 @@ export class FilePreviewScreenComponent implements OnInit {
return Object.keys(this.displayedAnnotations).map((key) => Number(key));
}
- public handleAnnotationSelected(annotation: AnnotationWrapper) {
- this.selectedAnnotation = annotation;
+ public handleAnnotationSelected(annotationId: string) {
+ this.selectedAnnotation = this.annotations.find((a) => a.id === annotationId);
this.scrollToSelectedAnnotation();
this._changeDetectorRef.detectChanges();
}
@@ -272,7 +274,8 @@ export class FilePreviewScreenComponent implements OnInit {
public rejectSuggestion($event: MouseEvent, annotation: AnnotationWrapper) {
this.ngZone.run(() => {
this._dialogRef = this._dialogService.rejectSuggestion($event, annotation, () => {
- this.activeViewer.annotManager.deleteAnnotation(annotation.annotation, false, true);
+ // TODO DELETE ANNOTATIOn
+ //this.activeViewer.annotManager.deleteAnnotation(annotation, false, true);
});
});
}
@@ -306,16 +309,17 @@ export class FilePreviewScreenComponent implements OnInit {
}
public get hasActiveFilters(): boolean {
- return AnnotationUtils.hasActiveFilters(this.filters);
+ // return AnnotationUtils.hasActiveFilters(this.filters);
+ return true;
}
- public hasSubsections(filter: AnnotationFilters | boolean) {
- return AnnotationUtils.hasSubsections(filter);
+ public hasSubsections(filter: AnnotationFilter[]) {
+ // return AnnotationUtils.hasSubsections(filter);
}
public setExpanded(key: string, value: boolean, $event: MouseEvent) {
$event.stopPropagation();
- this.expandedFilters[key] = value;
+ //this.expandedFilters[key] = value;
this._changeDetectorRef.detectChanges();
}
@@ -460,18 +464,6 @@ export class FilePreviewScreenComponent implements OnInit {
this.viewReady = true;
}
- handleAnnotationsAdded(annotations: AnnotationWrapper[]) {
- // replacing array causes UI flicker
- this.annotations.splice(0, this.annotations.length);
- this.annotations.push(...annotations);
- this.filters = this._filtersService.getFilters(
- this.appStateService.dictionaryData,
- this.annotations
- );
- this.applyFilters();
- this._changeDetectorRef.detectChanges();
- }
-
private _computeId(response: ManualAnnotationResponse) {
// if owner or not set the request prefix in the id
const prefix = this.appStateService.isActiveProjectOwner ? '' : 'request:add:';
@@ -498,4 +490,16 @@ export class FilePreviewScreenComponent implements OnInit {
const rgbColor = hexToRgb(color);
return new this.activeViewer.Annotations.Color(rgbColor.r, rgbColor.g, rgbColor.b);
}
+
+ filterClicked($event: MouseEvent, key: string, subkey?: string) {
+ $event.preventDefault();
+ $event.stopPropagation();
+ if (subkey) {
+ this.filters[key][subkey] = !this.filters[key][subkey];
+ } else {
+ //this.setAllFilters(this.filters[key],)
+ this.filters[key] = !this.filters[key];
+ }
+ return false;
+ }
}
diff --git a/apps/red-ui/src/app/screens/file/model/annotation.wrapper.ts b/apps/red-ui/src/app/screens/file/model/annotation.wrapper.ts
index 07174c644..f9da8f02a 100644
--- a/apps/red-ui/src/app/screens/file/model/annotation.wrapper.ts
+++ b/apps/red-ui/src/app/screens/file/model/annotation.wrapper.ts
@@ -1,5 +1,9 @@
-import { Annotations } from '@pdftron/webviewer';
-import { ManualRedactionEntry } from '@redaction/red-ui-http';
+import {
+ ManualRedactionEntry,
+ ManualRedactions,
+ Point,
+ RedactionLogEntry
+} from '@redaction/red-ui-http';
export class AnnotationWrapper {
superType: 'request' | 'redaction' | 'hint' | 'ignore';
@@ -8,25 +12,22 @@ export class AnnotationWrapper {
comments: string[] = [];
uuid: string;
manualRedactionEntry: ManualRedactionEntry;
+ firstTopLeftPoint: Point;
+ id: string;
+ content: string;
+ pageNumber;
- constructor(
- public annotation: Annotations.Annotation,
- manualRedactionEntries?: ManualRedactionEntry[]
- ) {
- this.comments = annotation['Mi'] ? annotation['Mi'].map((m) => m.eC) : [];
- const parts = annotation.Id.split(':');
- // first part is always the superType
- this.superType = parts[0].toLowerCase() as any;
- if (this.superType === 'redaction' || this.superType === 'hint') {
- this.dictionary = parts[1];
- }
- if (this.superType === 'request') {
- this.dictionary = parts[2] !== 'only_here' ? parts[2] : undefined;
- }
- this.uuid = parts[parts.length - 1];
- this.manualRedactionEntry = manualRedactionEntries
- ? manualRedactionEntries.find((e) => e.id === this.uuid)
- : undefined;
+ constructor(public redactionLogEntry: RedactionLogEntry, manualRedactions?: ManualRedactions) {
+ this.superType = redactionLogEntry.redacted
+ ? 'redaction'
+ : redactionLogEntry.hint
+ ? 'hint'
+ : 'ignore';
+ this.dictionary = redactionLogEntry.type;
+ this.firstTopLeftPoint = redactionLogEntry.positions[0]?.topLeft;
+ this.pageNumber = redactionLogEntry.positions[0]?.page;
+ this.id = redactionLogEntry.id;
+ this.content = redactionLogEntry.reason;
}
get manualRedactionOwner() {
@@ -34,22 +35,16 @@ export class AnnotationWrapper {
}
get x() {
- return this.annotation.getX();
+ return this.firstTopLeftPoint.x;
}
get y() {
- return this.annotation.getY();
+ return this.firstTopLeftPoint.y;
}
- get id() {
- return this.annotation.Id;
- }
-
- get content() {
- return this.annotation.getContents();
- }
-
- get pageNumber() {
- return this.annotation.PageNumber;
- }
+ // private String createAnnotationContent(Entity entity) {
+ //
+ // return "\nRule " + entity.getMatchedRule() + " matched\n\n" + entity.getRedactionReason() + "\n\nLegal basis:" + entity
+ // .getLegalBasis() + "\n\nIn section: \"" + entity.getHeadline() + "\"";
+ // }
}
diff --git a/apps/red-ui/src/app/screens/file/model/file-data.model.ts b/apps/red-ui/src/app/screens/file/model/file-data.model.ts
new file mode 100644
index 000000000..c15915b92
--- /dev/null
+++ b/apps/red-ui/src/app/screens/file/model/file-data.model.ts
@@ -0,0 +1,9 @@
+import { RedactionLog } from '@redaction/red-ui-http';
+
+export class FileDataModel {
+ constructor(
+ public annotatedFileData: Blob,
+ public redactedFileData: Blob,
+ public redactionLog: RedactionLog
+ ) {}
+}
diff --git a/apps/red-ui/src/app/screens/file/pdf-viewer/pdf-viewer.component.ts b/apps/red-ui/src/app/screens/file/pdf-viewer/pdf-viewer.component.ts
index e0f6bb6f6..9acb10c61 100644
--- a/apps/red-ui/src/app/screens/file/pdf-viewer/pdf-viewer.component.ts
+++ b/apps/red-ui/src/app/screens/file/pdf-viewer/pdf-viewer.component.ts
@@ -46,14 +46,12 @@ export interface ViewerState {
})
export class PdfViewerComponent implements OnInit, AfterViewInit, OnChanges {
private _viewerState: ViewerState = null; // no initial state
- private _annotationEventDebouncer = new Subject();
@Input() fileData: Blob;
@Input() fileStatus: FileStatus;
@Output() fileReady = new EventEmitter();
- @Output() annotationsAdded = new EventEmitter();
- @Output() annotationSelected = new EventEmitter();
+ @Output() annotationSelected = new EventEmitter();
@Output() manualAnnotationRequested = new EventEmitter();
@Output() pageChanged = new EventEmitter();
@Output() keyUp = new EventEmitter();
@@ -64,7 +62,6 @@ export class PdfViewerComponent implements OnInit, AfterViewInit, OnChanges {
@ViewChild('viewer', { static: true }) viewer: ElementRef;
instance: WebViewerInstance;
- private _manualAnnotations: ManualRedactions;
constructor(
private readonly _appStateService: AppStateService,
@@ -77,26 +74,6 @@ export class PdfViewerComponent implements OnInit, AfterViewInit, OnChanges {
ngOnInit() {
this._restoreViewerState = this._restoreViewerState.bind(this);
- // always publish all existing annotations this way everything gets drawn always
- this._annotationEventDebouncer.pipe(throttleTime(300)).subscribe((value) => {
- this.annotationsAdded.emit(
- AnnotationUtils.filterAndConvertAnnotations(
- this.instance.annotManager.getAnnotationsList(),
- this._manualAnnotationService.manualEntries
- )
- );
- // nasty double-emit fix, the annotationList is not updated when the event is fired
- setTimeout(
- () =>
- this.annotationsAdded.emit(
- AnnotationUtils.filterAndConvertAnnotations(
- this.instance.annotManager.getAnnotationsList(),
- this._manualAnnotationService.manualEntries
- )
- ),
- 200
- );
- });
this._manualAnnotationService.loadManualAnnotationsForActiveFile().subscribe(() => {});
}
@@ -124,15 +101,11 @@ export class PdfViewerComponent implements OnInit, AfterViewInit, OnChanges {
this._disableElements();
this._configureTextPopup();
this._configureHeader();
- instance.annotManager.on('annotationChanged', (annotations, action) => {
- this._annotationEventDebouncer.next(annotations);
- });
-
instance.annotManager.on('annotationSelected', (annotationList, action) => {
if (action === 'deselected') {
this.annotationSelected.emit(null);
} else {
- this.annotationSelected.emit(new AnnotationWrapper(annotationList[0]));
+ this.annotationSelected.emit(annotationList[0].Id);
}
});
@@ -273,10 +246,10 @@ export class PdfViewerComponent implements OnInit, AfterViewInit, OnChanges {
public selectAnnotation(annotation: AnnotationWrapper) {
this.instance.annotManager.deselectAllAnnotations();
- if (annotation?.annotation) {
- this.instance.annotManager.selectAnnotation(annotation.annotation);
- this.navigateToPage(annotation.pageNumber);
- }
+ const annotationFromViewer = this.instance.annotManager.getAnnotationById(annotation.id);
+ console.log(annotationFromViewer);
+ this.instance.annotManager.selectAnnotation(annotationFromViewer);
+ this.navigateToPage(annotation.pageNumber);
}
public navigateToPage(pageNumber: number) {
diff --git a/apps/red-ui/src/app/screens/file/service/file-download.service.ts b/apps/red-ui/src/app/screens/file/service/file-download.service.ts
index e5b923c8b..8a2b51918 100644
--- a/apps/red-ui/src/app/screens/file/service/file-download.service.ts
+++ b/apps/red-ui/src/app/screens/file/service/file-download.service.ts
@@ -1,14 +1,28 @@
import { Injectable } from '@angular/core';
-import { Observable, of } from 'rxjs';
-import { tap } from 'rxjs/operators';
-import { FileUploadControllerService } from '@redaction/red-ui-http';
+import { forkJoin, Observable, of } from 'rxjs';
+import { map, tap } from 'rxjs/operators';
+import { FileUploadControllerService, RedactionLogControllerService } from '@redaction/red-ui-http';
import { FileType } from '../model/file-type';
+import { FileDataModel } from '../model/file-data.model';
@Injectable({
providedIn: 'root'
})
export class FileDownloadService {
- constructor(private readonly _fileUploadControllerService: FileUploadControllerService) {}
+ constructor(
+ private readonly _fileUploadControllerService: FileUploadControllerService,
+ private readonly _redactionLogControllerService: RedactionLogControllerService
+ ) {}
+
+ public loadFileData(fileId: string): Observable {
+ const annotatedObs = this.loadFile('ANNOTATED', fileId);
+ const redactedObs = this.loadFile('REDACTED', fileId);
+ const reactionLogObs = this._redactionLogControllerService.getRedactionLog(fileId);
+
+ return forkJoin([annotatedObs, redactedObs, reactionLogObs]).pipe(
+ map((data) => new FileDataModel(...data))
+ );
+ }
loadFile(
fileType: FileType | string,
diff --git a/apps/red-ui/src/app/screens/file/service/filters.service.ts b/apps/red-ui/src/app/screens/file/service/filters.service.ts
index 4e15b32da..8b8d303b7 100644
--- a/apps/red-ui/src/app/screens/file/service/filters.service.ts
+++ b/apps/red-ui/src/app/screens/file/service/filters.service.ts
@@ -1,5 +1,5 @@
import { Injectable } from '@angular/core';
-import { AnnotationFilters } from '../../../utils/types';
+import { AnnotationFilter } from '../../../utils/types';
import { TypeValue } from '@redaction/red-ui-http';
import { AnnotationWrapper } from '../model/annotation.wrapper';
@@ -9,41 +9,33 @@ import { AnnotationWrapper } from '../model/annotation.wrapper';
export class FiltersService {
constructor() {}
- private _filters: AnnotationFilters = {
- hint: {},
- redaction: {},
- request: false,
- ignore: false
- };
-
public getFilters(
dictionaryData: { [key: string]: TypeValue },
annotations?: AnnotationWrapper[]
- ): AnnotationFilters {
+ ): AnnotationFilter[] {
const availableAnnotationTypes: Set = new Set();
annotations?.forEach((a) => {
availableAnnotationTypes.add(a.superType);
availableAnnotationTypes.add(a.dictionary);
});
- const filtersCopy = JSON.parse(JSON.stringify(this._filters));
+ const filters: AnnotationFilter[] = [];
for (const key of Object.keys(dictionaryData)) {
if (availableAnnotationTypes.has(key)) {
const typeValue = dictionaryData[key];
- if (typeValue.hint === true) {
- filtersCopy.hint[key] = typeValue.defaultFilter;
- }
- if (typeValue.hint === false) {
- filtersCopy.redaction[key] = typeValue.defaultFilter;
- }
+ const filter: AnnotationFilter = this._addOrGetGroup(
+ filters,
+ typeValue.hint ? 'hint' : 'redaction'
+ );
+ filter.filters.push({
+ key: key
+ });
}
}
- for (const key of Object.keys(filtersCopy)) {
- if (!availableAnnotationTypes.has(key)) {
- delete filtersCopy[key];
- }
- }
+ return filters;
+ }
- return filtersCopy;
+ private _addOrGetGroup(filters: AnnotationFilter[], name: string) {
+ return { key: name, filters: [] };
}
}
diff --git a/apps/red-ui/src/app/utils/annotation-utils.ts b/apps/red-ui/src/app/utils/annotation-utils.ts
index 0b4ca1ffc..ae675f0ad 100644
--- a/apps/red-ui/src/app/utils/annotation-utils.ts
+++ b/apps/red-ui/src/app/utils/annotation-utils.ts
@@ -1,56 +1,53 @@
-import { Annotations } from '@pdftron/webviewer';
-import { AnnotationFilters } from './types';
import { AnnotationWrapper } from '../screens/file/model/annotation.wrapper';
-import { ManualRedactionEntry } from '@redaction/red-ui-http';
+import { AnnotationFilter } from './types';
export class AnnotationUtils {
public static sortAnnotations(annotations: AnnotationWrapper[]): AnnotationWrapper[] {
return annotations.sort((ann1, ann2) => {
if (ann1.pageNumber === ann2.pageNumber) {
- if (ann1.x === ann2.y) {
- if (ann1.x === ann2.y) {
- return 0;
- }
- return ann1.x < ann2.x ? -1 : 1;
+ if (ann1.y === ann2.y) {
+ return ann1.x < ann2.x ? 1 : -1;
+ } else {
+ return ann1.y < ann2.y ? 1 : -1;
}
- return ann1.y < ann2.y ? -1 : 1;
}
return ann1.pageNumber < ann2.pageNumber ? -1 : 1;
});
}
- public static hasSubsections(filter: AnnotationFilters | boolean) {
+ public static hasSubsections(filter: AnnotationFilter | boolean) {
return filter instanceof Object;
}
- public static checkedSubkeys(filter: AnnotationFilters | boolean) {
+ public static checkedSubkeys(filter: AnnotationFilter | boolean) {
return Object.keys(filter).filter((subkey) => this.isChecked(filter[subkey])).length;
}
// Only some of the sub-items are selected
- public static isIndeterminate(filter: AnnotationFilters | boolean): boolean {
+ public static isIndeterminate(filter: AnnotationFilter | boolean): boolean {
return this.hasSubsections(filter)
? AnnotationUtils.checkedSubkeys(filter) > 0 && !this.isChecked(filter)
: false;
}
// All sub-items are selected
- public static isChecked(filter: AnnotationFilters | boolean): boolean {
+ public static isChecked(filter: AnnotationFilter | boolean): boolean {
return this.hasSubsections(filter)
? AnnotationUtils.checkedSubkeys(filter) === Object.keys(filter).length
: (filter as boolean);
}
- public static hasActiveFilters(filter: AnnotationFilters): boolean {
+ public static hasActiveFilters(filter: AnnotationFilter[]): boolean {
const activeFilters = Object.keys(filter).filter((key) => {
return this.isChecked(filter[key]) || this.isIndeterminate(filter[key]);
});
- return activeFilters.length > 0;
+ // return activeFilters.length > 0;
+ return false;
}
public static parseAnnotations(
annotations: AnnotationWrapper[],
- filters: AnnotationFilters
+ filters: AnnotationFilter[]
): { [key: number]: { annotations: AnnotationWrapper[] } } {
const obj = {};
@@ -86,19 +83,8 @@ export class AnnotationUtils {
obj[page].annotations = this.sortAnnotations(obj[page].annotations);
});
+ console.log(obj);
+
return obj;
}
-
- public static filterAndConvertAnnotations(
- annotations: Annotations.Annotation[],
- manualRedactions: ManualRedactionEntry[]
- ) {
- const convertedAnnotations: AnnotationWrapper[] = [];
- for (const annotation of annotations) {
- if (annotation.Id.indexOf(':') > 0) {
- convertedAnnotations.push(new AnnotationWrapper(annotation, manualRedactions));
- }
- }
- return convertedAnnotations;
- }
}
diff --git a/apps/red-ui/src/app/utils/types.d.ts b/apps/red-ui/src/app/utils/types.d.ts
index 40d6aa603..01afaabba 100644
--- a/apps/red-ui/src/app/utils/types.d.ts
+++ b/apps/red-ui/src/app/utils/types.d.ts
@@ -8,6 +8,9 @@ export class SortingOption {
column: string;
}
-export class AnnotationFilters {
- [key: string]: boolean | {};
+export interface AnnotationFilter {
+ key: string;
+ checked?: boolean;
+ indeterminate?: boolean;
+ filters?: AnnotationFilter[];
}
diff --git a/docker/common/nginx/nginx.conf.template b/docker/common/nginx/nginx.conf.template
index a76a14591..4ece652cb 100644
--- a/docker/common/nginx/nginx.conf.template
+++ b/docker/common/nginx/nginx.conf.template
@@ -44,6 +44,9 @@ server {
location /status {
proxy_pass $API_URL;
}
+ location /redactionLog {
+ proxy_pass $API_URL;
+ }
client_max_body_size 0;
gzip_min_length 1000;
gzip on;