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 9bbb97652..c93edcf27 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
@@ -88,7 +88,7 @@
>
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 6870bafb3..e08c31d30 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
@@ -119,23 +119,22 @@ export class FilePreviewScreenComponent implements OnInit {
private _loadFileData() {
return this._fileDownloadService.loadActiveFileData().pipe(
tap((fileDataModel) => {
- this.fileData = fileDataModel;
- this._rebuildFilters();
+ if (fileDataModel.fileStatus.isWorkable) {
+ this.fileData = fileDataModel;
+ this._rebuildFilters();
+ } else {
+ if (fileDataModel.fileStatus.isError) {
+ this._router.navigate(['/ui/projects/' + this.appStateService.activeProjectId]);
+ } else {
+ this.loadingMessage = 'file-preview.reanalyse-file';
+ }
+ }
})
);
}
private _rebuildFilters() {
- const manualRedactionAnnotations = this.fileData.entriesToAdd.map((mr) =>
- AnnotationWrapper.fromManualRedaction(mr, this.fileData.manualRedactions, this.appStateService.dictionaryData, this.permissionsService.currentUser)
- );
- const redactionLogAnnotations = this.fileData.redactionLogEntry.map((rde) =>
- AnnotationWrapper.fromRedactionLog(rde, this.fileData.manualRedactions, this.permissionsService.currentUser)
- );
-
- this.annotations = [];
- this.annotations.push(...manualRedactionAnnotations);
- this.annotations.push(...redactionLogAnnotations);
+ this.annotations = this.fileData.getAnnotations(this.appStateService.dictionaryData, this.permissionsService.currentUser);
this.filters = this._annotationProcessingService.getAnnotationFilter(this.annotations);
this.filtersChanged(this.filters);
}
@@ -371,17 +370,20 @@ export class FilePreviewScreenComponent implements OnInit {
private _cleanupAndRedrawManualAnnotations() {
this._fileDownloadService.loadActiveFileManualAnnotations().subscribe((manualRedactions) => {
const annotationsToRemove = [];
- this.fileData.entriesToAdd.forEach((manuallyAddedEntry) => {
- const annotation = this.activeViewer.annotManager.getAnnotationById(manuallyAddedEntry.id);
+ const previouslyDrawnAnnotations = this.annotations.filter((a) => a.shouldDraw);
+ previouslyDrawnAnnotations.forEach((annotationWrapper) => {
+ const annotation = this.activeViewer.annotManager.getAnnotationById(annotationWrapper.id);
if (annotation) {
annotationsToRemove.push(annotation);
}
});
this.activeViewer.annotManager.deleteAnnotations(annotationsToRemove, false, true);
-
this.fileData.manualRedactions = manualRedactions;
- this._annotationDrawService.drawAnnotations(this.instance, this.fileData.entriesToAdd);
this._rebuildFilters();
+ this._annotationDrawService.drawAnnotations(
+ this.instance,
+ this.annotations.filter((a) => a.shouldDraw)
+ );
});
}
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 cb2441843..222f5b2af 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,6 @@
-import { Comment, ManualRedactionEntry, ManualRedactions, Point, RedactionLogEntry, TypeValue } from '@redaction/red-ui-http';
+import { Comment, FileStatus, IdRemoval, ManualRedactionEntry, Point, Rectangle, RedactionLogEntry, TypeValue } from '@redaction/red-ui-http';
import { UserWrapper } from '../../../user/user.service';
+import { FileStatusWrapper } from './file-status.wrapper';
export const SuperTypeSorter = {
redaction: 1,
@@ -14,71 +15,120 @@ export class AnnotationWrapper {
dictionary: string;
color: string;
comments: Comment[] = [];
- manualRedactionEntry: ManualRedactionEntry;
firstTopLeftPoint: Point;
id: string;
content: string;
manual: boolean;
userId: string;
canUndo: boolean;
- manualAddToDictionary: boolean;
+ modifyDictionary: boolean;
typeLabel: string;
pageNumber: number;
hint: boolean;
+ shouldDraw: boolean;
redaction: boolean;
+ positions: Rectangle[];
- static fromRedactionLog(redactionLogEntry: RedactionLogEntry, manualRedactions: ManualRedactions, user: UserWrapper) {
- const comments: { [key: string]: Array } = manualRedactions.comments;
+ static fromData(
+ user: UserWrapper,
+ dictionaryData: { [p: string]: TypeValue },
+ fileStatus: FileStatusWrapper,
+ redactionLogEntry?: RedactionLogEntry,
+ manualRedactionEntry?: ManualRedactionEntry,
+ idRemoval?: IdRemoval,
+ comments?: Comment[]
+ ) {
const annotationWrapper = new AnnotationWrapper();
- annotationWrapper.id = redactionLogEntry.id;
- annotationWrapper.superType = redactionLogEntry.redacted ? 'redaction' : redactionLogEntry.hint ? 'hint' : 'ignore';
- annotationWrapper.redaction = redactionLogEntry.redacted;
- annotationWrapper.hint = redactionLogEntry.hint;
- annotationWrapper.dictionary = redactionLogEntry.type;
- annotationWrapper.firstTopLeftPoint = redactionLogEntry.positions[0]?.topLeft;
- annotationWrapper.pageNumber = redactionLogEntry.positions[0]?.page;
- annotationWrapper.manual = false;
- annotationWrapper.content =
- annotationWrapper.superType === 'redaction' || annotationWrapper.superType === 'ignore'
- ? AnnotationWrapper.createAnnotationContentForRedactionLogEntry(redactionLogEntry)
- : null;
+ annotationWrapper.comments = comments ? comments : [];
+
+ if (redactionLogEntry) {
+ annotationWrapper.id = redactionLogEntry.id;
+ annotationWrapper.redaction = redactionLogEntry.redacted;
+ annotationWrapper.hint = redactionLogEntry.hint;
+ annotationWrapper.dictionary = redactionLogEntry.type;
+ annotationWrapper.firstTopLeftPoint = redactionLogEntry.positions[0]?.topLeft;
+ annotationWrapper.pageNumber = redactionLogEntry.positions[0]?.page;
+ annotationWrapper.positions = redactionLogEntry.positions;
+ annotationWrapper.content = AnnotationWrapper.createContentForRedactionLog(redactionLogEntry);
+ // either marked as manual or idRemove or manualRedactionEntry exists
+ annotationWrapper.manual = redactionLogEntry.manual || !!manualRedactionEntry || !!idRemoval;
+ annotationWrapper.modifyDictionary = !!manualRedactionEntry?.addToDictionary || !!idRemoval?.removeFromDictionary;
+ switch (redactionLogEntry.status) {
+ case 'REQUESTED':
+ annotationWrapper.superType = idRemoval?.status === 'REQUESTED' || idRemoval?.status === 'APPROVED' ? 'suggestion-remove' : 'suggestion';
+ break;
+ case 'APPROVED':
+ annotationWrapper.superType = annotationWrapper.redaction ? 'redaction' : annotationWrapper.hint ? 'hint' : 'ignore';
+ break;
+ case 'DECLINED':
+ // TODO check this
+ annotationWrapper.superType = 'ignore';
+ break;
+ default:
+ annotationWrapper.superType =
+ idRemoval?.status === 'REQUESTED' || idRemoval?.status === 'APPROVED'
+ ? 'suggestion-remove'
+ : annotationWrapper.redaction
+ ? 'redaction'
+ : annotationWrapper.hint
+ ? 'hint'
+ : 'ignore';
+ break;
+ }
+ } else {
+ const dictionary = dictionaryData[manualRedactionEntry.type];
+ annotationWrapper.id = manualRedactionEntry.id;
+ annotationWrapper.redaction = !dictionary.hint;
+ annotationWrapper.hint = dictionary.hint;
+ annotationWrapper.dictionary = manualRedactionEntry.type;
+ annotationWrapper.firstTopLeftPoint = manualRedactionEntry.positions[0]?.topLeft;
+ annotationWrapper.pageNumber = manualRedactionEntry.positions[0]?.page;
+ annotationWrapper.positions = manualRedactionEntry.positions;
+ annotationWrapper.content = manualRedactionEntry.addToDictionary ? null : AnnotationWrapper.createContentForManualRedaction(manualRedactionEntry);
+ annotationWrapper.manual = true;
+ annotationWrapper.comments = comments[manualRedactionEntry.id] ? comments[manualRedactionEntry.id] : [];
+ annotationWrapper.userId = manualRedactionEntry.user;
+ annotationWrapper.canUndo = manualRedactionEntry?.user === user.id;
+ annotationWrapper.shouldDraw = AnnotationWrapper._shouldDraw(manualRedactionEntry, fileStatus);
+ annotationWrapper.modifyDictionary = manualRedactionEntry.addToDictionary;
+ switch (manualRedactionEntry.status) {
+ case 'REQUESTED':
+ annotationWrapper.superType = idRemoval?.status === 'REQUESTED' || idRemoval?.status === 'APPROVED' ? 'suggestion-remove' : 'suggestion';
+ break;
+ case 'APPROVED':
+ annotationWrapper.superType = redactionLogEntry.redacted ? 'redaction' : redactionLogEntry.hint ? 'hint' : 'ignore';
+ break;
+ case 'DECLINED':
+ // TODO check this
+ annotationWrapper.superType = 'ignore';
+ break;
+ default:
+ annotationWrapper.superType =
+ idRemoval?.status === 'REQUESTED' || idRemoval?.status === 'APPROVED'
+ ? 'suggestion-remove'
+ : annotationWrapper.redaction
+ ? 'redaction'
+ : annotationWrapper.hint
+ ? 'hint'
+ : 'ignore';
+ break;
+ }
+ }
- const toRemove = AnnotationWrapper._handleRemoveSuperType(annotationWrapper, manualRedactions, user);
- annotationWrapper.manualAddToDictionary = toRemove ? toRemove.removeFromDictionary : false;
- annotationWrapper.comments = comments[redactionLogEntry.id] ? comments[redactionLogEntry.id] : [];
AnnotationWrapper._setTypeLabel(annotationWrapper);
+
return annotationWrapper;
}
- static fromManualRedaction(
- manualRedactionEntry: ManualRedactionEntry,
- manualRedactions: ManualRedactions,
- dictionaryData: { [p: string]: TypeValue },
- user: UserWrapper
- ) {
- const comments: { [key: string]: Array } = manualRedactions.comments;
- const annotationWrapper = new AnnotationWrapper();
- annotationWrapper.id = manualRedactionEntry.id;
- annotationWrapper.superType = AnnotationWrapper.getManualRedactionSuperType(manualRedactionEntry, dictionaryData);
- const dictionary = dictionaryData[manualRedactionEntry.type];
- annotationWrapper.redaction = !dictionary.hint;
- annotationWrapper.hint = dictionary.hint;
- AnnotationWrapper._handleRemoveSuperType(annotationWrapper, manualRedactions, user);
- annotationWrapper.dictionary = manualRedactionEntry.type;
- annotationWrapper.firstTopLeftPoint = manualRedactionEntry.positions[0]?.topLeft;
- annotationWrapper.pageNumber = manualRedactionEntry.positions[0]?.page;
- annotationWrapper.content = manualRedactionEntry.addToDictionary
- ? null
- : AnnotationWrapper.createAnnotationContentForManualRedaction(manualRedactionEntry);
- annotationWrapper.manual = true;
- annotationWrapper.comments = comments[manualRedactionEntry.id] ? comments[manualRedactionEntry.id] : [];
- annotationWrapper.userId = manualRedactionEntry.user;
- annotationWrapper.canUndo = !manualRedactionEntry.processedDate && manualRedactionEntry.user === user.id;
+ private static _shouldDraw(manualRedaction: ManualRedactionEntry, fileStatus: FileStatus): boolean {
+ const isRequested =
+ manualRedaction.status === 'REQUESTED' && new Date(manualRedaction.requestDate).getTime() > new Date(fileStatus.lastProcessed).getTime();
+ const isApprovedOrDeclined =
+ (manualRedaction.status === 'APPROVED' || manualRedaction.status === 'DECLINED') &&
+ new Date(manualRedaction.requestDate).getTime() > new Date(fileStatus.lastProcessed).getTime();
- annotationWrapper.manualAddToDictionary = manualRedactionEntry.addToDictionary;
- AnnotationWrapper._setTypeLabel(annotationWrapper);
- return annotationWrapper;
+ return isRequested || isApprovedOrDeclined;
}
private static _setTypeLabel(annotationWrapper: AnnotationWrapper) {
@@ -91,7 +141,7 @@ export class AnnotationWrapper {
}
if (annotationWrapper.redaction) {
label += '-redaction';
- if (annotationWrapper.manualAddToDictionary) {
+ if (annotationWrapper.modifyDictionary) {
label += '-dictionary';
}
}
@@ -100,35 +150,8 @@ export class AnnotationWrapper {
annotationWrapper.typeLabel = label;
}
- private static _handleRemoveSuperType(annotationWrapper: AnnotationWrapper, manualRedactions: ManualRedactions, user: UserWrapper) {
- const toRemove = manualRedactions.idsToRemove.find((trm) => trm.id === annotationWrapper.id);
-
- // change super-type based on toRemove
- annotationWrapper.superType = toRemove
- ? toRemove.status === 'REQUESTED'
- ? 'suggestion-remove'
- : toRemove.status === 'APPROVED'
- ? 'ignore'
- : annotationWrapper.superType
- : annotationWrapper.superType;
-
- if (toRemove) {
- annotationWrapper.canUndo = !toRemove.processedDate && toRemove.user === user.id;
- }
- return toRemove;
- }
-
- static getManualRedactionSuperType(manualRedactionEntry: ManualRedactionEntry, dictionaryData: { [p: string]: TypeValue }) {
- const dictionary = dictionaryData[manualRedactionEntry.type];
- return manualRedactionEntry.status === 'REQUESTED' ? 'suggestion' : dictionary.hint ? 'hint' : 'redaction';
- }
-
constructor() {}
- get manualRedactionOwner() {
- return this.manualRedactionEntry?.user;
- }
-
get x() {
return this.firstTopLeftPoint.x;
}
@@ -137,14 +160,13 @@ export class AnnotationWrapper {
return this.firstTopLeftPoint.y;
}
- private static createAnnotationContentForManualRedaction(entry: ManualRedactionEntry) {
- return entry.reason;
- // + '\n\nLegal basis:' +
- // entry.legalBasis
+ private static createContentForRedactionLog(entry: RedactionLogEntry) {
+ if (entry.redacted) {
+ return entry.reason + '\n\nLegal basis:' + entry.legalBasis + '\n\nIn section: "' + entry.section + '"';
+ }
}
- private static createAnnotationContentForRedactionLogEntry(entry: RedactionLogEntry) {
- // return "\nRule " + entry.matchedRule + " matched\n\n" + entry.reason + "\n\nLegal basis:" + entry.legalBasis + "\n\nIn section: \"" + entry.section + "\"";
- return entry.reason + '\n\nLegal basis:' + entry.legalBasis + '\n\nIn section: "' + entry.section + '"';
+ private static createContentForManualRedaction(entry: ManualRedactionEntry) {
+ return entry.reason + '\n\nLegal basis:' + entry.legalBasis;
}
}
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
index 5c4b4a2eb..ed841df3e 100644
--- 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
@@ -1,5 +1,14 @@
-import { ManualRedactionEntry, ManualRedactions, RedactionLog, RedactionLogEntry, ViewedPages } from '@redaction/red-ui-http';
+import { Comment, IdRemoval, ManualRedactionEntry, ManualRedactions, RedactionLog, RedactionLogEntry, TypeValue, ViewedPages } from '@redaction/red-ui-http';
import { FileStatusWrapper } from './file-status.wrapper';
+import { UserWrapper } from '../../../user/user.service';
+import { AnnotationWrapper } from './annotation.wrapper';
+
+export interface AnnotationPair {
+ redactionLogEntry?: RedactionLogEntry;
+ manualRedactionEntry?: ManualRedactionEntry;
+ idRemoval?: IdRemoval;
+ comments?: Comment[];
+}
export class FileDataModel {
constructor(
@@ -12,18 +21,54 @@ export class FileDataModel {
) {}
get redactionLogEntry(): RedactionLogEntry[] {
- return this.redactionLog.redactionLogEntry.filter((r) => r.status !== 'REQUESTED');
+ return this.redactionLog.redactionLogEntry;
}
- get entriesToAdd(): ManualRedactionEntry[] {
- return this.manualRedactions.entriesToAdd.filter((manualRedaction) => {
- const isRequested =
- manualRedaction.status === 'REQUESTED' && new Date(manualRedaction.requestDate).getTime() > new Date(this.fileStatus.lastProcessed).getTime();
- const isApprovedOrDeclined =
- (manualRedaction.status === 'APPROVED' || manualRedaction.status === 'DECLINED') &&
- new Date(manualRedaction.requestDate).getTime() > new Date(this.fileStatus.lastProcessed).getTime();
+ getAnnotations(dictionaryData: { [p: string]: TypeValue }, currentUser: UserWrapper): AnnotationWrapper[] {
+ const annotations = [];
- return isRequested || isApprovedOrDeclined;
+ const pairs: AnnotationPair[] = this._createPairs();
+
+ pairs.forEach((pair) => {
+ const annotation = AnnotationWrapper.fromData(
+ currentUser,
+ dictionaryData,
+ this.fileStatus,
+ pair.redactionLogEntry,
+ pair.manualRedactionEntry,
+ pair.idRemoval,
+ pair.comments
+ );
+ annotations.push(annotation);
});
+
+ return annotations;
+ }
+
+ private _createPairs(): AnnotationPair[] {
+ const pairs: AnnotationPair[] = [];
+
+ this.redactionLog.redactionLogEntry.forEach((rdl) => {
+ pairs.push({
+ redactionLogEntry: rdl,
+ manualRedactionEntry: this.manualRedactions.entriesToAdd.find((eta) => eta.id === rdl.id),
+ idRemoval: this.manualRedactions.idsToRemove.find((idr) => idr.id === rdl.id),
+ comments: this.manualRedactions.comments[rdl.id]
+ });
+ });
+
+ this.manualRedactions.entriesToAdd.forEach((eta) => {
+ const redactionLogEntry = this.redactionLog.redactionLogEntry.find((rdl) => rdl.id === eta.id);
+ if (!redactionLogEntry) {
+ pairs.push({
+ redactionLogEntry: null,
+ manualRedactionEntry: eta,
+ idRemoval: this.manualRedactions.idsToRemove.find((idr) => idr.id === eta.id),
+ comments: this.manualRedactions.comments[eta.id]
+ });
+ }
+ });
+
+ return pairs;
}
}
diff --git a/apps/red-ui/src/app/screens/file/service/annotation-draw.service.ts b/apps/red-ui/src/app/screens/file/service/annotation-draw.service.ts
index e6344018b..a590745e9 100644
--- a/apps/red-ui/src/app/screens/file/service/annotation-draw.service.ts
+++ b/apps/red-ui/src/app/screens/file/service/annotation-draw.service.ts
@@ -11,55 +11,42 @@ import { AnnotationWrapper } from '../model/annotation.wrapper';
export class AnnotationDrawService {
constructor(private readonly _appStateService: AppStateService) {}
- public drawAnnotations(activeViewer: WebViewerInstance, mres: ManualRedactionEntry[]) {
- mres.forEach((mre) => {
+ public drawAnnotations(activeViewer: WebViewerInstance, annotationWrappers: AnnotationWrapper[]) {
+ annotationWrappers.forEach((mre) => {
this.drawAnnotation(activeViewer, mre);
});
}
- public drawAnnotation(activeViewer: WebViewerInstance, mre: ManualRedactionEntry) {
- const pageNumber = mre.positions[0].page;
+ public drawAnnotation(activeViewer: WebViewerInstance, annotationWrapper: AnnotationWrapper) {
+ const pageNumber = annotationWrapper.pageNumber;
const highlight = new activeViewer.Annotations.TextHighlightAnnotation();
highlight.PageNumber = pageNumber;
- highlight.StrokeColor = this._getColor(activeViewer, mre);
- highlight.setContents(mre.reason);
- highlight.Quads = this._rectanglesToQuads(mre.positions, activeViewer, pageNumber);
- highlight.Id = mre.id;
+ highlight.StrokeColor = this._getColor(activeViewer, annotationWrapper);
+ highlight.setContents(annotationWrapper.content);
+ highlight.Quads = this._rectanglesToQuads(annotationWrapper.positions, activeViewer, pageNumber);
+ highlight.Id = annotationWrapper.id;
const annotationManager = activeViewer.annotManager;
annotationManager.addAnnotation(highlight, true);
annotationManager.redrawAnnotation(highlight);
}
- private _getColor(activeViewer: WebViewerInstance, manualRedactionEntry: ManualRedactionEntry) {
- // if you're the owner, use the request color, otherwise use the actual dict color
- const superType = AnnotationWrapper.getManualRedactionSuperType(
- manualRedactionEntry,
- this._appStateService.dictionaryData
- );
+ private _getColor(activeViewer: WebViewerInstance, annotationWrapper: AnnotationWrapper) {
const color =
- superType === 'suggestion'
- ? this._appStateService.getDictionaryColor(superType)
- : this._appStateService.getDictionaryColor(manualRedactionEntry.type);
+ annotationWrapper.superType === 'suggestion' || annotationWrapper.superType === 'suggestion-remove'
+ ? this._appStateService.getDictionaryColor(annotationWrapper.superType)
+ : this._appStateService.getDictionaryColor(annotationWrapper.dictionary);
const rgbColor = hexToRgb(color);
return new activeViewer.Annotations.Color(rgbColor.r, rgbColor.g, rgbColor.b);
}
- private _rectanglesToQuads(
- positions: Rectangle[],
- activeViewer: WebViewerInstance,
- pageNumber: number
- ): any[] {
+ private _rectanglesToQuads(positions: Rectangle[], activeViewer: WebViewerInstance, pageNumber: number): any[] {
const pageHeight = activeViewer.docViewer.getPageHeight(pageNumber);
return positions.map((p) => this._rectangleToQuad(p, activeViewer, pageHeight));
}
- private _rectangleToQuad(
- rectangle: Rectangle,
- activeViewer: WebViewerInstance,
- pageHeight: number
- ): any {
+ private _rectangleToQuad(rectangle: Rectangle, activeViewer: WebViewerInstance, pageHeight: number): any {
const x1 = rectangle.topLeft.x;
const y1 = pageHeight - (rectangle.topLeft.y + rectangle.height);