265 lines
11 KiB
TypeScript
265 lines
11 KiB
TypeScript
import {
|
|
IdRemoval,
|
|
ManualRedactionEntry,
|
|
ManualRedactions,
|
|
RedactionChangeLog,
|
|
RedactionLog,
|
|
ViewedPages
|
|
} from '@redaction/red-ui-http';
|
|
import { FileStatusWrapper } from './file-status.wrapper';
|
|
import { UserWrapper } from '@services/user.service';
|
|
import { AnnotationWrapper } from './annotation.wrapper';
|
|
import { RedactionLogEntryWrapper } from './redaction-log-entry.wrapper';
|
|
import { ViewMode } from './view-mode';
|
|
import { TypeValueWrapper } from './type-value.wrapper';
|
|
|
|
export class AnnotationData {
|
|
visibleAnnotations: AnnotationWrapper[];
|
|
allAnnotations: AnnotationWrapper[];
|
|
}
|
|
|
|
export class FileDataModel {
|
|
constructor(
|
|
public fileStatus: FileStatusWrapper,
|
|
public fileData: Blob,
|
|
public redactionLog: RedactionLog,
|
|
public redactionChangeLog: RedactionChangeLog,
|
|
public manualRedactions: ManualRedactions,
|
|
public viewedPages?: ViewedPages
|
|
) {}
|
|
|
|
getAnnotations(
|
|
dictionaryData: { [p: string]: TypeValueWrapper },
|
|
currentUser: UserWrapper,
|
|
viewMode: ViewMode,
|
|
areDevFeaturesEnabled: boolean
|
|
): AnnotationData {
|
|
const entries: RedactionLogEntryWrapper[] = this._convertData(dictionaryData);
|
|
let allAnnotations = entries.map(entry => AnnotationWrapper.fromData(entry));
|
|
|
|
if (!areDevFeaturesEnabled) {
|
|
allAnnotations = allAnnotations.filter(annotation => !annotation.isFalsePositive);
|
|
}
|
|
|
|
const visibleAnnotations = allAnnotations.filter(annotation => {
|
|
if (viewMode === 'STANDARD') {
|
|
return !annotation.isChangeLogRemoved;
|
|
} else if (viewMode === 'DELTA') {
|
|
return annotation.isChangeLogEntry;
|
|
} else {
|
|
return annotation.isRedacted;
|
|
}
|
|
});
|
|
|
|
return {
|
|
visibleAnnotations: visibleAnnotations,
|
|
allAnnotations: allAnnotations
|
|
};
|
|
}
|
|
|
|
private _convertData(dictionaryData: {
|
|
[p: string]: TypeValueWrapper;
|
|
}): RedactionLogEntryWrapper[] {
|
|
let result: RedactionLogEntryWrapper[] = [];
|
|
|
|
this.redactionChangeLog?.redactionLogEntry?.forEach(changeLogEntry => {
|
|
if (changeLogEntry.changeType === 'REMOVED') {
|
|
// Fix backend issue where some annotations are both added and removed
|
|
const sameEntryExistsAsAdd = !!this.redactionChangeLog.redactionLogEntry.find(
|
|
rle => rle.id === changeLogEntry.id && rle.changeType === 'ADDED'
|
|
);
|
|
if (sameEntryExistsAsAdd) {
|
|
return;
|
|
}
|
|
|
|
const redactionLogEntryWrapper: RedactionLogEntryWrapper = {
|
|
actionPendingReanalysis: false
|
|
};
|
|
|
|
Object.assign(redactionLogEntryWrapper, changeLogEntry);
|
|
|
|
redactionLogEntryWrapper.comments =
|
|
this.manualRedactions.comments[redactionLogEntryWrapper.id];
|
|
redactionLogEntryWrapper.isChangeLogEntry = true;
|
|
redactionLogEntryWrapper.changeLogType = changeLogEntry.changeType;
|
|
redactionLogEntryWrapper.id = 'changed-log-removed-' + redactionLogEntryWrapper.id;
|
|
result.push(redactionLogEntryWrapper);
|
|
}
|
|
});
|
|
|
|
this.redactionLog.redactionLogEntry?.forEach(redactionLogEntry => {
|
|
// false positive entries from the redaction-log need to be skipped
|
|
if (redactionLogEntry.type?.toLowerCase() === 'false_positive') {
|
|
return;
|
|
}
|
|
|
|
const existingChangeLogEntry = this.redactionChangeLog?.redactionLogEntry?.find(
|
|
rle => rle.id === redactionLogEntry.id
|
|
);
|
|
|
|
// copy the redactionLog Entry
|
|
const redactionLogEntryWrapper: RedactionLogEntryWrapper = {
|
|
actionPendingReanalysis: false
|
|
};
|
|
Object.assign(redactionLogEntryWrapper, redactionLogEntry);
|
|
redactionLogEntryWrapper.comments =
|
|
this.manualRedactions.comments[redactionLogEntryWrapper.id];
|
|
redactionLogEntryWrapper.isChangeLogEntry = !!existingChangeLogEntry;
|
|
redactionLogEntryWrapper.changeLogType = 'ADDED';
|
|
result.push(redactionLogEntryWrapper);
|
|
});
|
|
|
|
this.manualRedactions.forceRedactions?.forEach(forceRedaction => {
|
|
const relevantRedactionLogEntry = result.find(r => r.id === forceRedaction.id);
|
|
|
|
if (forceRedaction.status === 'DECLINED') {
|
|
relevantRedactionLogEntry.status = 'DECLINED';
|
|
relevantRedactionLogEntry.userId = forceRedaction.user;
|
|
relevantRedactionLogEntry.dictionaryEntry = false;
|
|
relevantRedactionLogEntry.force = true;
|
|
return;
|
|
}
|
|
|
|
// an entry for this request already exists in the redactionLog
|
|
if (relevantRedactionLogEntry) {
|
|
relevantRedactionLogEntry.userId = forceRedaction.user;
|
|
relevantRedactionLogEntry.dictionaryEntry = false;
|
|
relevantRedactionLogEntry.force = true;
|
|
|
|
// if statuses differ
|
|
if (
|
|
!forceRedaction.processedDate ||
|
|
forceRedaction.status !== relevantRedactionLogEntry.status
|
|
) {
|
|
relevantRedactionLogEntry.actionPendingReanalysis = true;
|
|
relevantRedactionLogEntry.status = forceRedaction.status;
|
|
}
|
|
}
|
|
});
|
|
|
|
this.manualRedactions.entriesToAdd?.forEach(manual => {
|
|
const markedAsReasonRedactionLogEntry = result.find(r => r.id === manual.reason);
|
|
|
|
const relevantRedactionLogEntry = result.find(r => r.id === manual.id);
|
|
|
|
// a redaction-log entry is marked as a reason for another entry - hide it
|
|
if (markedAsReasonRedactionLogEntry) {
|
|
if (!(this._hasAlreadyBeenProcessed(manual) && manual.status === 'APPROVED')) {
|
|
markedAsReasonRedactionLogEntry.hidden = true;
|
|
}
|
|
}
|
|
|
|
// an entry for this request already exists in the redactionLog
|
|
if (relevantRedactionLogEntry) {
|
|
if (relevantRedactionLogEntry.status === 'DECLINED') {
|
|
relevantRedactionLogEntry.hidden = true;
|
|
return;
|
|
}
|
|
|
|
relevantRedactionLogEntry.userId = manual.user;
|
|
relevantRedactionLogEntry.dictionaryEntry = manual.addToDictionary;
|
|
|
|
// if statuses differ
|
|
if (relevantRedactionLogEntry.status !== manual.status) {
|
|
relevantRedactionLogEntry.actionPendingReanalysis = true;
|
|
relevantRedactionLogEntry.status = manual.status;
|
|
}
|
|
} else {
|
|
// dictionary modifying requests that have been processed already updated
|
|
// the dictionary and should not be drawn
|
|
if (manual.addToDictionary && this._hasAlreadyBeenProcessed(manual)) {
|
|
return;
|
|
}
|
|
|
|
// no entry exists in the redaction log - create it
|
|
const dictionary = dictionaryData[manual.type];
|
|
|
|
const redactionLogEntryWrapper: RedactionLogEntryWrapper = {};
|
|
|
|
redactionLogEntryWrapper.id = manual.id;
|
|
redactionLogEntryWrapper.dictionaryEntry = manual.addToDictionary;
|
|
redactionLogEntryWrapper.legalBasis = manual.legalBasis;
|
|
redactionLogEntryWrapper.positions = manual.positions;
|
|
redactionLogEntryWrapper.reason = manual.reason;
|
|
redactionLogEntryWrapper.status = manual.status;
|
|
redactionLogEntryWrapper.type = manual.type;
|
|
redactionLogEntryWrapper.userId = manual.user;
|
|
redactionLogEntryWrapper.value = manual.value;
|
|
redactionLogEntryWrapper.redacted = !dictionary.hint;
|
|
redactionLogEntryWrapper.hint = dictionary.hint;
|
|
redactionLogEntryWrapper.manualRedactionType = 'ADD';
|
|
redactionLogEntryWrapper.manual = true;
|
|
redactionLogEntryWrapper.comments =
|
|
this.manualRedactions.comments[redactionLogEntryWrapper.id];
|
|
if (markedAsReasonRedactionLogEntry) {
|
|
// cleanup reason if the reason is another annotationId
|
|
// it is not needed for drawing
|
|
redactionLogEntryWrapper.reason = null;
|
|
}
|
|
|
|
result.push(redactionLogEntryWrapper);
|
|
}
|
|
});
|
|
|
|
this.manualRedactions.idsToRemove?.forEach(idToRemove => {
|
|
const relevantRedactionLogEntry = result.find(r => r.id === idToRemove.id);
|
|
|
|
if (!relevantRedactionLogEntry) {
|
|
// idRemove for something that doesn't exist - skip
|
|
return;
|
|
} else {
|
|
relevantRedactionLogEntry.userId = idToRemove.user;
|
|
relevantRedactionLogEntry.dictionaryEntry = idToRemove.removeFromDictionary;
|
|
|
|
// if statuses differ
|
|
if (relevantRedactionLogEntry.status !== idToRemove.status) {
|
|
relevantRedactionLogEntry.actionPendingReanalysis = true;
|
|
relevantRedactionLogEntry.status = idToRemove.status;
|
|
}
|
|
|
|
if (this._hasAlreadyBeenProcessed(idToRemove)) {
|
|
if (idToRemove.status === 'DECLINED') {
|
|
relevantRedactionLogEntry.status = null;
|
|
}
|
|
}
|
|
}
|
|
});
|
|
|
|
result.forEach(redactionLogEntry => {
|
|
if (redactionLogEntry.manual) {
|
|
if (redactionLogEntry.manualRedactionType === 'ADD') {
|
|
const foundManualEntry = this.manualRedactions.entriesToAdd.find(
|
|
me => me.id === redactionLogEntry.id
|
|
);
|
|
// ADD has been undone - not yet processed
|
|
if (!foundManualEntry) {
|
|
redactionLogEntry.hidden = true;
|
|
}
|
|
}
|
|
if (redactionLogEntry.manualRedactionType === 'REMOVE') {
|
|
const foundManualEntry = this.manualRedactions.idsToRemove.find(
|
|
me => me.id === redactionLogEntry.id
|
|
);
|
|
// REMOVE has been undone - not yet processed
|
|
if (!foundManualEntry) {
|
|
redactionLogEntry.manual = false;
|
|
redactionLogEntry.manualRedactionType = 'UNDO';
|
|
redactionLogEntry.status = null;
|
|
}
|
|
}
|
|
}
|
|
});
|
|
|
|
// remove undone entriesToAdd and idsToRemove
|
|
result = result.filter(redactionLogEntry => !redactionLogEntry.hidden);
|
|
return result;
|
|
}
|
|
|
|
private _hasAlreadyBeenProcessed(entry: ManualRedactionEntry | IdRemoval): boolean {
|
|
return !entry.processedDate
|
|
? false
|
|
: new Date(entry.processedDate).getTime() <
|
|
new Date(this.fileStatus.lastProcessed).getTime();
|
|
}
|
|
}
|