Integrated manual changes for annotations

This commit is contained in:
Timo Bejan 2022-01-28 11:16:08 +02:00
parent 33e49adc3b
commit 58c56b7f7b
14 changed files with 350 additions and 301 deletions

View File

@ -1,13 +1,14 @@
import { RedactionLogEntryWrapper } from './redaction-log-entry.wrapper';
import { annotationTypesTranslations } from '../../translations/annotation-types-translations'; import { annotationTypesTranslations } from '../../translations/annotation-types-translations';
import { marker as _ } from '@biesbjerg/ngx-translate-extract-marker'; import { marker as _ } from '@biesbjerg/ngx-translate-extract-marker';
import { IComment, IPoint, IRectangle } from '@red/domain'; import { IComment, IManualChange, IPoint, IRectangle, LogEntryStatus, ManualRedactionType } from '@red/domain';
import { RedactionLogEntry } from '@models/file/redaction-log.entry';
export type AnnotationSuperType = export type AnnotationSuperType =
| 'suggestion-change-legal-basis' | 'suggestion-change-legal-basis'
| 'suggestion-recategorize-image' | 'suggestion-recategorize-image'
| 'suggestion-add-dictionary' | 'suggestion-add-dictionary'
| 'suggestion-force-redaction' | 'suggestion-force-redaction'
| 'suggestion-force-hint'
| 'suggestion-resize' | 'suggestion-resize'
| 'suggestion-remove-dictionary' | 'suggestion-remove-dictionary'
| 'suggestion-add' | 'suggestion-add'
@ -44,15 +45,14 @@ export class AnnotationWrapper {
legalBasisChangeValue?: string; legalBasisChangeValue?: string;
resizing?: boolean; resizing?: boolean;
rectangle?: boolean; rectangle?: boolean;
hintDictionary?: boolean;
section?: string; section?: string;
reference: Array<string>; reference: Array<string>;
manual?: boolean;
image?: boolean; image?: boolean;
manual?: boolean;
hidden?: boolean; hidden?: boolean;
force?: boolean; pending?: boolean;
hintDictionary?: boolean;
textAfter?: string; textAfter?: string;
textBefore?: string; textBefore?: string;
@ -64,11 +64,10 @@ export class AnnotationWrapper {
hasBeenResized: boolean; hasBeenResized: boolean;
hasBeenRecategorized: boolean; hasBeenRecategorized: boolean;
hasLegalBasisChanged: boolean; hasLegalBasisChanged: boolean;
hasBeenForced: boolean; hasBeenForcedHint: boolean;
hasBeenForcedRedaction: boolean;
hasBeenRemovedByManualOverride: boolean; hasBeenRemovedByManualOverride: boolean;
private _origin: RedactionLogEntryWrapper;
get isChangeLogRemoved() { get isChangeLogRemoved() {
return this.changeLogType === 'REMOVED'; return this.changeLogType === 'REMOVED';
} }
@ -204,7 +203,8 @@ export class AnnotationWrapper {
this.hasBeenResized || this.hasBeenResized ||
this.hasBeenRecategorized || this.hasBeenRecategorized ||
this.hasLegalBasisChanged || this.hasLegalBasisChanged ||
this.hasBeenForced || this.hasBeenForcedHint ||
this.hasBeenForcedRedaction ||
this.hasBeenRemovedByManualOverride this.hasBeenRemovedByManualOverride
); );
} }
@ -233,41 +233,48 @@ export class AnnotationWrapper {
return this.legalBasisChangeValue || this.legalBasisValue; return this.legalBasisChangeValue || this.legalBasisValue;
} }
static fromData(redactionLogEntry?: RedactionLogEntryWrapper) { static fromData(redactionLogEntry?: RedactionLogEntry) {
const annotationWrapper = new AnnotationWrapper(); const annotationWrapper = new AnnotationWrapper();
annotationWrapper._origin = redactionLogEntry;
annotationWrapper.annotationId = redactionLogEntry.id; annotationWrapper.annotationId = redactionLogEntry.id;
annotationWrapper.isChangeLogEntry = redactionLogEntry.isChangeLogEntry; annotationWrapper.isChangeLogEntry = redactionLogEntry.isChangeLogEntry;
annotationWrapper.changeLogType = redactionLogEntry.changeLogType; annotationWrapper.changeLogType = redactionLogEntry.changeLogType;
annotationWrapper.redaction = redactionLogEntry.redacted; annotationWrapper.redaction = redactionLogEntry.redacted;
annotationWrapper.hint = redactionLogEntry.hint; annotationWrapper.hint = redactionLogEntry.hint;
annotationWrapper.typeValue = redactionLogEntry.type; annotationWrapper.typeValue = redactionLogEntry.type;
annotationWrapper.recategorizationType = redactionLogEntry.recategorizationType;
annotationWrapper.value = redactionLogEntry.value; annotationWrapper.value = redactionLogEntry.value;
annotationWrapper.firstTopLeftPoint = redactionLogEntry.positions[0]?.topLeft; annotationWrapper.firstTopLeftPoint = redactionLogEntry.positions[0]?.topLeft;
annotationWrapper.pageNumber = redactionLogEntry.positions[0]?.page; annotationWrapper.pageNumber = redactionLogEntry.positions[0]?.page;
annotationWrapper.positions = redactionLogEntry.positions; annotationWrapper.positions = redactionLogEntry.positions;
annotationWrapper.status = redactionLogEntry.status;
annotationWrapper.textBefore = redactionLogEntry.textBefore; annotationWrapper.textBefore = redactionLogEntry.textBefore;
annotationWrapper.textAfter = redactionLogEntry.textAfter; annotationWrapper.textAfter = redactionLogEntry.textAfter;
annotationWrapper.dictionaryOperation = redactionLogEntry.dictionaryEntry; annotationWrapper.dictionaryOperation = redactionLogEntry.dictionaryEntry;
annotationWrapper.image = redactionLogEntry.image; annotationWrapper.image = redactionLogEntry.image;
annotationWrapper.legalBasisChangeValue = redactionLogEntry.legalBasisChangeValue;
annotationWrapper.legalBasisValue = redactionLogEntry.legalBasis; annotationWrapper.legalBasisValue = redactionLogEntry.legalBasis;
annotationWrapper.comments = redactionLogEntry.comments || []; annotationWrapper.comments = redactionLogEntry.comments || [];
annotationWrapper.manual = redactionLogEntry.manual; annotationWrapper.manual = redactionLogEntry.manualChanges.length > 0;
annotationWrapper.engines = redactionLogEntry.engines; annotationWrapper.engines = redactionLogEntry.engines;
annotationWrapper.section = redactionLogEntry.section; annotationWrapper.section = redactionLogEntry.section;
annotationWrapper.reference = redactionLogEntry.reference || []; annotationWrapper.reference = redactionLogEntry.reference || [];
annotationWrapper.rectangle = redactionLogEntry.rectangle; annotationWrapper.rectangle = redactionLogEntry.rectangle;
annotationWrapper.hasBeenResized = redactionLogEntry.hasBeenResized; annotationWrapper.hasBeenResized = !!redactionLogEntry.manualChanges.find(
annotationWrapper.hasBeenRecategorized = redactionLogEntry.hasBeenRecategorized; c => c.manualRedactionType === ManualRedactionType.RESIZE && c.annotationStatus === LogEntryStatus.APPROVED,
annotationWrapper.hasLegalBasisChanged = redactionLogEntry.hasLegalBasisChanged; );
annotationWrapper.hasBeenForced = redactionLogEntry.hasBeenForced; annotationWrapper.hasBeenRecategorized = !!redactionLogEntry.manualChanges.find(
annotationWrapper.hasBeenRemovedByManualOverride = redactionLogEntry.hasBeenRemovedByManualOverride; c => c.manualRedactionType === ManualRedactionType.RECATEGORIZE && c.annotationStatus === LogEntryStatus.APPROVED,
annotationWrapper.hintDictionary = redactionLogEntry.hintDictionary; );
annotationWrapper.hasLegalBasisChanged = !!redactionLogEntry.manualChanges.find(
c => c.manualRedactionType === ManualRedactionType.LEGAL_BASIS_CHANGE && c.annotationStatus === LogEntryStatus.APPROVED,
);
annotationWrapper.hasBeenForcedHint = !!redactionLogEntry.manualChanges.find(
c => c.manualRedactionType === ManualRedactionType.FORCE_HINT && c.annotationStatus === LogEntryStatus.APPROVED,
);
annotationWrapper.hasBeenForcedRedaction = !!redactionLogEntry.manualChanges.find(
c => c.manualRedactionType === ManualRedactionType.FORCE_REDACT && c.annotationStatus === LogEntryStatus.APPROVED,
);
annotationWrapper.hasBeenRemovedByManualOverride = !!redactionLogEntry.manualChanges.find(
c => c.manualRedactionType === ManualRedactionType.REMOVE_LOCALLY && c.annotationStatus === LogEntryStatus.APPROVED,
);
this._createContent(annotationWrapper, redactionLogEntry); this._createContent(annotationWrapper, redactionLogEntry);
this._setSuperType(annotationWrapper, redactionLogEntry); this._setSuperType(annotationWrapper, redactionLogEntry);
@ -277,167 +284,38 @@ export class AnnotationWrapper {
return annotationWrapper; return annotationWrapper;
} }
private static _handleRecommendations(annotationWrapper: AnnotationWrapper, redactionLogEntry: RedactionLogEntryWrapper) { private static _handleRecommendations(annotationWrapper: AnnotationWrapper, redactionLogEntry: RedactionLogEntry) {
if (annotationWrapper.superType === 'recommendation') { if (annotationWrapper.superType === 'recommendation') {
annotationWrapper.recommendationType = redactionLogEntry.type.substr('recommendation_'.length); annotationWrapper.recommendationType = redactionLogEntry.type.substr('recommendation_'.length);
} }
} }
private static _setSuperType(annotationWrapper: AnnotationWrapper, redactionLogEntryWrapper: RedactionLogEntryWrapper) { private static _setSuperType(annotationWrapper: AnnotationWrapper, redactionLogEntryWrapper: RedactionLogEntry) {
if (redactionLogEntryWrapper.recommendation) { if (redactionLogEntryWrapper.recommendation) {
annotationWrapper.superType = 'recommendation'; annotationWrapper.superType = 'recommendation';
return; return;
} }
if (redactionLogEntryWrapper.manualRedactionType === 'RESIZE') { if (redactionLogEntryWrapper.manualChanges.length) {
if (redactionLogEntryWrapper.status === 'REQUESTED') { const lastManualChange = redactionLogEntryWrapper.manualChanges[redactionLogEntryWrapper.manualChanges.length - 1];
annotationWrapper.superType = 'suggestion-resize';
return;
}
}
if (redactionLogEntryWrapper.manualRedactionType === 'FORCE_REDACT') { annotationWrapper.pending = !lastManualChange.processed;
annotationWrapper.force = true;
if (redactionLogEntryWrapper.status === 'REQUESTED') { annotationWrapper.superType = AnnotationWrapper._selectSuperType(redactionLogEntryWrapper, lastManualChange);
annotationWrapper.superType = 'suggestion-force-redaction'; } else {
} else if (redactionLogEntryWrapper.status === 'APPROVED') { if (redactionLogEntryWrapper.recommendation) {
annotationWrapper.superType = redactionLogEntryWrapper.hint ? 'hint' : 'redaction'; annotationWrapper.superType = 'recommendation';
} else { } else if (redactionLogEntryWrapper.redacted) {
annotationWrapper.superType = 'skipped';
}
return;
}
if (annotationWrapper.type?.toLowerCase() === 'false_positive') {
if (redactionLogEntryWrapper.status === 'REQUESTED') {
annotationWrapper.superType = 'suggestion-add-dictionary';
return;
}
if (redactionLogEntryWrapper.status === 'DECLINED') {
annotationWrapper.superType = 'declined-suggestion';
return;
}
if (!redactionLogEntryWrapper.manual) {
annotationWrapper.superType = 'skipped';
}
return;
}
if (redactionLogEntryWrapper.type === 'manual') {
if (redactionLogEntryWrapper.status === 'REQUESTED') {
annotationWrapper.superType = 'suggestion-add';
return;
}
if (redactionLogEntryWrapper.status === 'APPROVED') {
annotationWrapper.superType = 'manual-redaction';
return;
}
if (redactionLogEntryWrapper.status === 'DECLINED') {
annotationWrapper.superType = 'declined-suggestion';
return;
}
}
if (redactionLogEntryWrapper.manualRedactionType === 'LEGAL_BASIS_CHANGE') {
if (redactionLogEntryWrapper.status === 'REQUESTED') {
annotationWrapper.superType = 'suggestion-change-legal-basis';
} else {
annotationWrapper.superType = 'redaction'; annotationWrapper.superType = 'redaction';
} } else if (redactionLogEntryWrapper.hint) {
return; annotationWrapper.superType = 'hint';
}
if (redactionLogEntryWrapper.manualRedactionType === 'RECATEGORIZE') {
if (redactionLogEntryWrapper.status === 'REQUESTED') {
annotationWrapper.superType = 'suggestion-recategorize-image';
return;
}
if (redactionLogEntryWrapper.status === 'APPROVED') {
annotationWrapper.superType = annotationWrapper.redaction ? 'redaction' : annotationWrapper.hint ? 'hint' : 'skipped';
return;
}
}
if (redactionLogEntryWrapper.manualRedactionType === 'ADD') {
if (redactionLogEntryWrapper.dictionaryEntry) {
if (redactionLogEntryWrapper.status === 'REQUESTED') {
annotationWrapper.superType = 'suggestion-add-dictionary';
return;
}
if (redactionLogEntryWrapper.status === 'DECLINED') {
annotationWrapper.superType = 'declined-suggestion';
return;
}
} else { } else {
if (redactionLogEntryWrapper.status === 'REQUESTED') { annotationWrapper.superType = 'skipped';
annotationWrapper.superType = 'suggestion-add';
return;
}
if (redactionLogEntryWrapper.status === 'APPROVED') {
annotationWrapper.superType = 'manual-redaction';
return;
}
if (redactionLogEntryWrapper.status === 'DECLINED') {
annotationWrapper.superType = 'declined-suggestion';
return;
}
}
}
if (!redactionLogEntryWrapper.redacted && !redactionLogEntryWrapper.hint) {
if (redactionLogEntryWrapper.status === 'REQUESTED') {
if (redactionLogEntryWrapper.dictionaryEntry) {
annotationWrapper.superType =
redactionLogEntryWrapper.manualRedactionType === 'ADD'
? 'suggestion-add-dictionary'
: 'suggestion-remove-dictionary';
}
}
}
if (redactionLogEntryWrapper.redacted || redactionLogEntryWrapper.hint) {
if (redactionLogEntryWrapper.status === 'REQUESTED') {
if (redactionLogEntryWrapper.dictionaryEntry) {
annotationWrapper.superType =
redactionLogEntryWrapper.manualRedactionType === 'ADD'
? 'suggestion-add-dictionary'
: 'suggestion-remove-dictionary';
} else {
annotationWrapper.superType =
redactionLogEntryWrapper.manualRedactionType === 'ADD' ? 'suggestion-add' : 'suggestion-remove';
}
return;
}
if (redactionLogEntryWrapper.status === 'APPROVED') {
if (redactionLogEntryWrapper.dictionaryEntry) {
annotationWrapper.superType = annotationWrapper.redaction ? 'redaction' : annotationWrapper.hint ? 'hint' : 'skipped';
} else {
annotationWrapper.superType =
redactionLogEntryWrapper.manualRedactionType === 'ADD'
? 'manual-redaction'
: annotationWrapper.redaction
? 'redaction'
: annotationWrapper.hint
? 'hint'
: 'skipped';
}
return;
}
}
if (!annotationWrapper.superType) {
annotationWrapper.superType = annotationWrapper.redaction ? 'redaction' : annotationWrapper.hint ? 'hint' : 'skipped';
}
if (annotationWrapper.superType === 'skipped') {
if (redactionLogEntryWrapper.hintDictionary) {
annotationWrapper.superType = 'ignored-hint';
} }
} }
} }
private static _createContent(annotationWrapper: AnnotationWrapper, entry: RedactionLogEntryWrapper) { private static _createContent(annotationWrapper: AnnotationWrapper, entry: RedactionLogEntry) {
let content = ''; let content = '';
if (entry.matchedRule) { if (entry.matchedRule) {
content += `Rule ${entry.matchedRule} matched \n\n`; content += `Rule ${entry.matchedRule} matched \n\n`;
@ -459,7 +337,7 @@ export class AnnotationWrapper {
annotationWrapper.content = content; annotationWrapper.content = content;
} }
private static _getShortContent(annotationWrapper: AnnotationWrapper, entry: RedactionLogEntryWrapper) { private static _getShortContent(annotationWrapper: AnnotationWrapper, entry: RedactionLogEntry) {
if (annotationWrapper.legalBasis) { if (annotationWrapper.legalBasis) {
const lb = entry.legalBasisList?.find(lbm => lbm.reason.toLowerCase().includes(annotationWrapper.legalBasis.toLowerCase())); const lb = entry.legalBasisList?.find(lbm => lbm.reason.toLowerCase().includes(annotationWrapper.legalBasis.toLowerCase()));
if (lb) { if (lb) {
@ -469,4 +347,121 @@ export class AnnotationWrapper {
return annotationWrapper.legalBasis; return annotationWrapper.legalBasis;
} }
// | 'suggestion-change-legal-basis'
// | 'suggestion-recategorize-image'
// | 'suggestion-add-dictionary'
// | 'suggestion-force-redaction'
// | 'suggestion-resize'
// | 'suggestion-remove-dictionary'
// | 'suggestion-add'
// | 'suggestion-remove'
// | 'ignored-hint'
// | 'skipped'
// | 'redaction'
// | 'manual-redaction'
// | 'recommendation'
// | 'hint'
// | 'declined-suggestion';
private static _selectSuperType(redactionLogEntry: RedactionLogEntry, lastManualChange: IManualChange): AnnotationSuperType {
switch (lastManualChange.manualRedactionType) {
case ManualRedactionType.ADD_LOCALLY:
switch (lastManualChange.annotationStatus) {
case LogEntryStatus.APPROVED:
return 'manual-redaction';
case LogEntryStatus.DECLINED:
return 'declined-suggestion';
case LogEntryStatus.REQUESTED:
return 'suggestion-add';
}
case ManualRedactionType.ADD_TO_DICTIONARY:
switch (lastManualChange.annotationStatus) {
case LogEntryStatus.APPROVED:
// TODO
return 'redaction';
case LogEntryStatus.DECLINED:
return 'declined-suggestion';
case LogEntryStatus.REQUESTED:
return 'suggestion-add-dictionary';
}
break;
case ManualRedactionType.REMOVE_LOCALLY:
switch (lastManualChange.annotationStatus) {
case LogEntryStatus.APPROVED:
return 'skipped';
case LogEntryStatus.DECLINED:
return 'declined-suggestion';
case LogEntryStatus.REQUESTED:
return 'suggestion-remove';
}
break;
case ManualRedactionType.REMOVE_FROM_DICTIONARY:
switch (lastManualChange.annotationStatus) {
case LogEntryStatus.APPROVED:
// TODO
return 'skipped';
case LogEntryStatus.DECLINED:
return 'declined-suggestion';
case LogEntryStatus.REQUESTED:
return 'suggestion-remove-dictionary';
}
break;
case ManualRedactionType.FORCE_REDACT:
switch (lastManualChange.annotationStatus) {
case LogEntryStatus.APPROVED:
// TODO
return 'redaction';
case LogEntryStatus.DECLINED:
return 'declined-suggestion';
case LogEntryStatus.REQUESTED:
return 'suggestion-force-redaction';
}
break;
case ManualRedactionType.FORCE_HINT:
switch (lastManualChange.annotationStatus) {
case LogEntryStatus.APPROVED:
// TODO
return 'redaction';
case LogEntryStatus.DECLINED:
return 'declined-suggestion';
case LogEntryStatus.REQUESTED:
return 'suggestion-force-hint';
}
break;
case ManualRedactionType.RECATEGORIZE:
switch (lastManualChange.annotationStatus) {
case LogEntryStatus.APPROVED:
return redactionLogEntry.redacted ? 'redaction' : 'hint';
case LogEntryStatus.DECLINED:
return 'declined-suggestion';
case LogEntryStatus.REQUESTED:
return 'suggestion-recategorize-image';
}
break;
case ManualRedactionType.LEGAL_BASIS_CHANGE:
switch (lastManualChange.annotationStatus) {
case LogEntryStatus.APPROVED:
return 'redaction';
case LogEntryStatus.DECLINED:
return 'declined-suggestion';
case LogEntryStatus.REQUESTED:
return 'suggestion-change-legal-basis';
}
break;
case ManualRedactionType.RESIZE:
switch (lastManualChange.annotationStatus) {
case LogEntryStatus.APPROVED:
if (redactionLogEntry.recommendation) {
return 'recommendation';
} else {
return redactionLogEntry.redacted ? 'redaction' : 'hint';
}
case LogEntryStatus.DECLINED:
return 'declined-suggestion';
case LogEntryStatus.REQUESTED:
return 'suggestion-resize';
}
break;
}
}
} }

View File

@ -1,8 +1,18 @@
import { Dictionary, File, IRedactionLog, IRedactionLogEntry, IViewedPage, ViewMode } from '@red/domain'; import {
ChangeType,
Dictionary,
File,
IRedactionLog,
IRedactionLogEntry,
IViewedPage,
LogEntryStatus,
ManualRedactionType,
ViewMode,
} from '@red/domain';
import { AnnotationWrapper } from './annotation.wrapper'; import { AnnotationWrapper } from './annotation.wrapper';
import { RedactionLogEntryWrapper } from './redaction-log-entry.wrapper';
import * as moment from 'moment'; import * as moment from 'moment';
import { BehaviorSubject } from 'rxjs'; import { BehaviorSubject } from 'rxjs';
import { RedactionLogEntry } from './redaction-log.entry';
export class FileDataModel { export class FileDataModel {
static readonly DELTA_VIEW_TIME = 10 * 60 * 1000; // 10 minutes; static readonly DELTA_VIEW_TIME = 10 * 60 * 1000; // 10 minutes;
@ -54,7 +64,7 @@ export class FileDataModel {
} }
private _buildAllAnnotations() { private _buildAllAnnotations() {
const entries: RedactionLogEntryWrapper[] = this._convertData(); const entries: RedactionLogEntry[] = this._convertData();
const previousAnnotations = this.allAnnotations || []; const previousAnnotations = this.allAnnotations || [];
this.allAnnotations = entries this.allAnnotations = entries
@ -77,31 +87,30 @@ export class FileDataModel {
}); });
} }
private _convertData(): RedactionLogEntryWrapper[] { private _convertData(): RedactionLogEntry[] {
let result: RedactionLogEntryWrapper[] = []; let result: RedactionLogEntry[] = [];
const reasonAnnotationIds: { [key: string]: RedactionLogEntryWrapper[] } = {}; const reasonAnnotationIds: { [key: string]: RedactionLogEntry[] } = {};
this.redactionLog.redactionLogEntry?.forEach(redactionLogEntry => { this.redactionLog.redactionLogEntry?.forEach(redactionLogEntry => {
// copy the redactionLog Entry // copy the redactionLog Entry
const redactionLogEntryWrapper: RedactionLogEntryWrapper = {};
Object.assign(redactionLogEntryWrapper, redactionLogEntry);
redactionLogEntryWrapper.type = redactionLogEntry.type;
redactionLogEntryWrapper.hintDictionary = this._dictionaryData[redactionLogEntry.type].hint;
this._isChangeLogEntry(redactionLogEntry, redactionLogEntryWrapper); const changeLogValues = this.getChangeLogValues(redactionLogEntry);
const redactionLogEntryWrapper: RedactionLogEntry = new RedactionLogEntry(
redactionLogEntry,
changeLogValues.changeLogType,
changeLogValues.isChangeLogEntry,
changeLogValues.hidden,
this._dictionaryData[redactionLogEntry.type].hint,
this.redactionLog.legalBasis,
);
if ( if (
redactionLogEntryWrapper.status === 'DECLINED' && redactionLogEntry.manualChanges.find(
redactionLogEntryWrapper.manualRedactionType === 'ADD' && mc =>
redactionLogEntryWrapper.type === 'false_positive' mc.manualRedactionType === ManualRedactionType.ADD_TO_DICTIONARY &&
(mc.annotationStatus === LogEntryStatus.APPROVED || mc.annotationStatus === LogEntryStatus.REQUESTED),
)
) { ) {
// ignore these
return;
} else {
result.push(redactionLogEntryWrapper);
}
if (redactionLogEntry.manual && (redactionLogEntry.status === 'APPROVED' || redactionLogEntry.status === 'REQUESTED')) {
// for dictionary entries -> I.E accepted recommendations or false positives, // for dictionary entries -> I.E accepted recommendations or false positives,
// check reason // check reason
if (!reasonAnnotationIds[redactionLogEntry.reason]) { if (!reasonAnnotationIds[redactionLogEntry.reason]) {
@ -123,16 +132,16 @@ export class FileDataModel {
return !matched; return !matched;
}); });
result.forEach(redactionLogEntry => {
redactionLogEntry.legalBasisList = this.redactionLog.legalBasis;
});
result = result.filter(r => !r.hidden); result = result.filter(r => !r.hidden);
return result; return result;
} }
private _isChangeLogEntry(redactionLogEntry: IRedactionLogEntry, wrapper: RedactionLogEntryWrapper) { private getChangeLogValues(redactionLogEntry: IRedactionLogEntry): {
hidden: boolean;
changeLogType: ChangeType;
isChangeLogEntry: boolean;
} {
if (this._file.numberOfAnalyses > 1) { if (this._file.numberOfAnalyses > 1) {
const viableChanges = redactionLogEntry.changes.filter(c => c.analysisNumber > 1); const viableChanges = redactionLogEntry.changes.filter(c => c.analysisNumber > 1);
viableChanges.sort((a, b) => moment(a.dateTime).valueOf() - moment(b.dateTime).valueOf()); viableChanges.sort((a, b) => moment(a.dateTime).valueOf() - moment(b.dateTime).valueOf());
@ -150,19 +159,28 @@ export class FileDataModel {
// at least one unseen change // at least one unseen change
if (relevantChanges.length > 0) { if (relevantChanges.length > 0) {
// at least 1 relevant change // at least 1 relevant change
wrapper.changeLogType = relevantChanges[relevantChanges.length - 1].type;
wrapper.isChangeLogEntry = true;
viewedPage.showAsUnseen = moment(viewedPage.viewedTime).valueOf() < moment(lastChange.dateTime).valueOf(); viewedPage.showAsUnseen = moment(viewedPage.viewedTime).valueOf() < moment(lastChange.dateTime).valueOf();
this.hasChangeLog$.next(true); this.hasChangeLog$.next(true);
return {
changeLogType: relevantChanges[relevantChanges.length - 1].type,
isChangeLogEntry: true,
hidden: false,
};
} else { } else {
// no relevant changes - hide removed anyway // no relevant changes - hide removed anyway
wrapper.isChangeLogEntry = false; return {
wrapper.hidden = lastChange && lastChange.type === 'REMOVED'; changeLogType: null,
isChangeLogEntry: false,
hidden: lastChange && lastChange.type === 'REMOVED',
};
} }
} else { } else {
// Page doesn't have a view-time // Page doesn't have a view-time
wrapper.isChangeLogEntry = false; return {
wrapper.hidden = lastChange && lastChange.type === 'REMOVED'; changeLogType: null,
isChangeLogEntry: false,
hidden: lastChange && lastChange.type === 'REMOVED',
};
} }
} }
// console.log(wrapper.changeLogType, wrapper.hidden, wrapper.isChangeLogEntry, wrapper.value, lastChange); // console.log(wrapper.changeLogType, wrapper.hidden, wrapper.isChangeLogEntry, wrapper.value, lastChange);

View File

@ -1,56 +0,0 @@
import { IChange, IComment, ILegalBasis, IRectangle } from '@red/domain';
export interface RedactionLogEntryWrapper {
changes?: Array<IChange>;
dossierDictionaryEntry?: boolean;
endOffset?: number;
excluded?: boolean;
imageHasTransparency?: boolean;
manualRedactionUserId?: string;
reference?: Array<string>;
startOffset?: number;
type?: string;
rectangle?: boolean;
hintDictionary?: boolean;
color?: Array<number>;
dictionaryEntry?: boolean;
hint?: boolean;
id?: string;
legalBasis?: string;
manual?: boolean;
manualRedactionType?: 'ADD' | 'REMOVE' | 'LEGAL_BASIS_CHANGE' | 'FORCE_REDACT' | 'RECATEGORIZE' | 'RESIZE';
matchedRule?: number;
positions?: Array<IRectangle>;
reason?: string;
redacted?: boolean;
section?: string;
sectionNumber?: number;
status?: 'REQUESTED' | 'APPROVED' | 'DECLINED';
textAfter?: string;
textBefore?: string;
value?: string;
image?: boolean;
legalBasisList?: ILegalBasis[];
recommendation?: boolean;
recommendationAnnotationId?: string;
hidden?: boolean;
userId?: string;
comments?: IComment[];
isChangeLogEntry?: boolean;
changeLogType?: 'ADDED' | 'REMOVED' | 'CHANGED';
recategorizationType?: string;
legalBasisChangeValue?: string;
engines?: string[];
hasBeenResized?: boolean;
hasBeenRecategorized?: boolean;
hasLegalBasisChanged?: boolean;
hasBeenForced?: boolean;
hasBeenRemovedByManualOverride?: boolean;
}

View File

@ -0,0 +1,81 @@
import { IChange, IComment, ILegalBasis, IManualChange, IRectangle, IRedactionLogEntry, LogEntryEngine } from '@red/domain';
export class RedactionLogEntry implements IRedactionLogEntry {
readonly changes?: IChange[];
readonly manualChanges?: IManualChange[];
readonly color?: number[];
readonly comments?: IComment[];
readonly dictionaryEntry?: boolean;
readonly dossierDictionaryEntry?: boolean;
readonly endOffset?: number;
readonly engines?: LogEntryEngine[];
readonly excluded?: boolean;
readonly hint?: boolean;
readonly rectangle?: boolean;
readonly id?: string;
readonly image?: boolean;
readonly imageHasTransparency?: boolean;
readonly legalBasis?: string;
readonly matchedRule?: number;
readonly positions?: IRectangle[];
readonly recommendation?: boolean;
readonly redacted?: boolean;
readonly reference?: string[];
readonly section?: string;
readonly sectionNumber?: number;
readonly startOffset?: number;
readonly textAfter?: string;
readonly textBefore?: string;
readonly type?: string;
readonly value?: string;
readonly changeLogType?: 'ADDED' | 'REMOVED' | 'CHANGED';
readonly isChangeLogEntry?: boolean;
readonly hidden?: boolean;
readonly legalBasisList: ILegalBasis[];
readonly hintDictionary: boolean;
reason?: string;
constructor(
redactionLogEntry: IRedactionLogEntry,
changeLogType: 'ADDED' | 'REMOVED' | 'CHANGED',
isChangeLogEntry: boolean,
hidden: boolean,
hintDictionary: boolean,
legalBasisList: ILegalBasis[],
) {
this.changes = redactionLogEntry.changes;
this.manualChanges = redactionLogEntry.manualChanges;
this.color = redactionLogEntry.color;
this.comments = redactionLogEntry.comments;
this.dictionaryEntry = redactionLogEntry.dictionaryEntry;
this.dossierDictionaryEntry = redactionLogEntry.dossierDictionaryEntry;
this.endOffset = redactionLogEntry.endOffset;
this.engines = redactionLogEntry.engines;
this.excluded = redactionLogEntry.excluded;
this.hint = redactionLogEntry.hint;
this.rectangle = redactionLogEntry.rectangle;
this.id = redactionLogEntry.id;
this.image = redactionLogEntry.image;
this.imageHasTransparency = redactionLogEntry.imageHasTransparency;
this.legalBasis = redactionLogEntry.legalBasis;
this.matchedRule = redactionLogEntry.matchedRule;
this.positions = redactionLogEntry.positions;
this.reason = redactionLogEntry.reason;
this.recommendation = redactionLogEntry.recommendation;
this.redacted = redactionLogEntry.redacted;
this.reference = redactionLogEntry.reference;
this.section = redactionLogEntry.section;
this.sectionNumber = redactionLogEntry.sectionNumber;
this.startOffset = redactionLogEntry.startOffset;
this.textAfter = redactionLogEntry.textAfter;
this.textBefore = redactionLogEntry.textBefore;
this.type = redactionLogEntry.type;
this.value = redactionLogEntry.value;
this.changeLogType = changeLogType;
this.isChangeLogEntry = isChangeLogEntry;
this.hidden = hidden;
this.hintDictionary = hintDictionary;
this.legalBasisList = legalBasisList;
}
}

View File

@ -6,6 +6,7 @@ export const annotationChangesTranslations: { [key in KeysOf<AnnotationWrapper>]
hasBeenResized: _('annotation-changes.resized'), hasBeenResized: _('annotation-changes.resized'),
hasBeenRecategorized: _('annotation-changes.recategorized'), hasBeenRecategorized: _('annotation-changes.recategorized'),
hasLegalBasisChanged: _('annotation-changes.legal-basis'), hasLegalBasisChanged: _('annotation-changes.legal-basis'),
hasBeenForced: _('annotation-changes.forced'), hasBeenForcedRedaction: _('annotation-changes.forced-redaction'),
hasBeenForcedHint: _('annotation-changes.forced-hint'),
hasBeenRemovedByManualOverride: _('annotation-changes.removed-manual'), hasBeenRemovedByManualOverride: _('annotation-changes.removed-manual'),
} as const; } as const;

View File

@ -14,6 +14,7 @@ export const annotationTypesTranslations: { [key in AnnotationSuperType]: string
'suggestion-change-legal-basis': _('annotation-type.suggestion-change-legal-basis'), 'suggestion-change-legal-basis': _('annotation-type.suggestion-change-legal-basis'),
'suggestion-recategorize-image': _('annotation-type.suggestion-recategorize-image'), 'suggestion-recategorize-image': _('annotation-type.suggestion-recategorize-image'),
'suggestion-force-redaction': _('annotation-type.suggestion-force-redaction'), 'suggestion-force-redaction': _('annotation-type.suggestion-force-redaction'),
'suggestion-force-hint': _('annotation-type.suggestion-force-hint'),
'suggestion-remove': _('annotation-type.suggestion-remove'), 'suggestion-remove': _('annotation-type.suggestion-remove'),
'suggestion-remove-dictionary': _('annotation-type.suggestion-remove-dictionary'), 'suggestion-remove-dictionary': _('annotation-type.suggestion-remove-dictionary'),
'suggestion-resize': _('annotation-type.suggestion-resize'), 'suggestion-resize': _('annotation-type.suggestion-resize'),

View File

@ -3,7 +3,8 @@ import { AnnotationSuperType } from '../../models/file/annotation.wrapper';
export const SuperTypeSorter: { [key in AnnotationSuperType]: number } = { export const SuperTypeSorter: { [key in AnnotationSuperType]: number } = {
'suggestion-change-legal-basis': 14, 'suggestion-change-legal-basis': 14,
'suggestion-force-redaction': 15, 'suggestion-force-redaction': 15,
'suggestion-recategorize-image': 16, 'suggestion-force-hint': 16,
'suggestion-recategorize-image': 17,
'suggestion-resize': 18, 'suggestion-resize': 18,
recommendation: 7, recommendation: 7,
'suggestion-add-dictionary': 12, 'suggestion-add-dictionary': 12,
@ -14,6 +15,6 @@ export const SuperTypeSorter: { [key in AnnotationSuperType]: number } = {
skipped: 50, skipped: 50,
redaction: 1, redaction: 1,
'manual-redaction': 2, 'manual-redaction': 2,
hint: 17, hint: 19,
'declined-suggestion': 20, 'declined-suggestion': 20,
}; };

View File

@ -272,7 +272,8 @@
"undo": "Undo" "undo": "Undo"
}, },
"annotation-changes": { "annotation-changes": {
"forced": "Redaction forced", "forced-redaction": "Redaction forced",
"forced-hint": "Hint forced",
"header": "Manual changes:", "header": "Manual changes:",
"legal-basis": "Reason changed", "legal-basis": "Reason changed",
"recategorized": "Image category changed", "recategorized": "Image category changed",
@ -296,8 +297,9 @@
"suggestion-add-dictionary": "Suggested dictionary add", "suggestion-add-dictionary": "Suggested dictionary add",
"suggestion-change-legal-basis": "Suggested change legal basis", "suggestion-change-legal-basis": "Suggested change legal basis",
"suggestion-force-redaction": "Suggestion force redaction", "suggestion-force-redaction": "Suggestion force redaction",
"suggestion-force-redaction": "Suggestion force hint",
"suggestion-recategorize-image": "Suggested recategorize image", "suggestion-recategorize-image": "Suggested recategorize image",
"suggestion-remove": "Suggested hint removal", "suggestion-remove": "Suggested local removal",
"suggestion-remove-dictionary": "Suggested dictionary removal", "suggestion-remove-dictionary": "Suggested dictionary removal",
"suggestion-resize": "Suggested Resize" "suggestion-resize": "Suggested Resize"
}, },

@ -1 +1 @@
Subproject commit 6c79b02a50f268a6b663e8e46d88f3c0d333a608 Subproject commit 31b60ff117a8d8ecb9617be3b0c9e1822d437034

View File

@ -4,9 +4,8 @@ export interface IChange {
type?: ChangeType; type?: ChangeType;
} }
export const ChangeTypes = { export enum ChangeType {
ADDED: 'ADDED', ADDED = 'ADDED',
CHANGED: 'CHANGED', CHANGED = 'CHANGED',
REMOVED: 'REMOVED', REMOVED = 'REMOVED',
} as const; }
export type ChangeType = keyof typeof ChangeTypes;

View File

@ -10,3 +10,4 @@ export * from './manual-add.response';
export * from './approve-request'; export * from './approve-request';
export * from './image-recategorization.request'; export * from './image-recategorization.request';
export * from './resize.request'; export * from './resize.request';
export * from './manual-change';

View File

@ -0,0 +1,11 @@
import { LogEntryStatus, ManualRedactionType } from './types';
export interface IManualChange {
userId: string;
processedDate: string;
annotationStatus: LogEntryStatus;
manualRedactionType: ManualRedactionType;
propertyChanges: { [key: string]: any };
processed: boolean;
}

View File

@ -1,17 +1,18 @@
import { IChange } from './change'; import { IChange } from './change';
import { IComment } from './comment'; import { IComment } from './comment';
import { IRectangle } from '../geometry'; import { IRectangle } from '../geometry';
import { LogEntryEngine, LogEntryStatus, ManualRedactionType } from './types'; import { LogEntryEngine } from './types';
import { List } from '@iqser/common-ui'; import { IManualChange } from './manual-change';
export interface IRedactionLogEntry { export interface IRedactionLogEntry {
changes?: IChange[]; changes?: IChange[];
color?: List<number>; manualChanges?: IManualChange[];
comments?: List<IComment>; color?: number[];
comments?: IComment[];
dictionaryEntry?: boolean; dictionaryEntry?: boolean;
dossierDictionaryEntry?: boolean; dossierDictionaryEntry?: boolean;
endOffset?: number; endOffset?: number;
engines?: List<LogEntryEngine>; engines?: LogEntryEngine[];
excluded?: boolean; excluded?: boolean;
hint?: boolean; hint?: boolean;
rectangle?: boolean; rectangle?: boolean;
@ -19,21 +20,15 @@ export interface IRedactionLogEntry {
image?: boolean; image?: boolean;
imageHasTransparency?: boolean; imageHasTransparency?: boolean;
legalBasis?: string; legalBasis?: string;
legalBasisChangeValue?: string;
manual?: boolean;
manualRedactionType?: ManualRedactionType;
manualRedactionUserId?: string;
matchedRule?: number; matchedRule?: number;
positions?: List<IRectangle>; positions?: IRectangle[];
reason?: string; reason?: string;
recategorizationType?: string;
recommendation?: boolean; recommendation?: boolean;
redacted?: boolean; redacted?: boolean;
reference?: List; reference?: string[];
section?: string; section?: string;
sectionNumber?: number; sectionNumber?: number;
startOffset?: number; startOffset?: number;
status?: LogEntryStatus;
textAfter?: string; textAfter?: string;
textBefore?: string; textBefore?: string;
type?: string; type?: string;

View File

@ -1,23 +1,23 @@
export const LogEntryEngines = { export enum LogEntryEngine {
DICTIONARY: 'DICTIONARY', DICTIONARY = 'DICTIONARY',
NER: 'NER', NER = 'NER',
RULE: 'RULE', RULE = 'RULE',
}; }
export type LogEntryEngine = keyof typeof LogEntryEngines;
export const ManualRedactionTypes = { export enum ManualRedactionType {
ADD: 'ADD', ADD_LOCALLY = 'ADD_LOCALLY',
FORCE_REDACT: 'FORCE_REDACT', ADD_TO_DICTIONARY = 'ADD_TO_DICTIONARY',
LEGAL_BASIS_CHANGE: 'LEGAL_BASIS_CHANGE', REMOVE_LOCALLY = 'REMOVE_LOCALLY',
RECATEGORIZE: 'RECATEGORIZE', REMOVE_FROM_DICTIONARY = 'REMOVE_FROM_DICTIONARY',
REMOVE: 'REMOVE', FORCE_REDACT = 'FORCE_REDACT',
} as const; FORCE_HINT = 'FORCE_HINT',
export type ManualRedactionType = keyof typeof ManualRedactionTypes; RECATEGORIZE = 'RECATEGORIZE',
LEGAL_BASIS_CHANGE = 'LEGAL_BASIS_CHANGE',
RESIZE = 'RESIZE',
}
export const LogEntryStatuses = { export enum LogEntryStatus {
APPROVED: 'APPROVED', APPROVED = 'APPROVED',
DECLINED: 'DECLINED', DECLINED = 'DECLINED',
REQUESTED: 'REQUESTED', REQUESTED = 'REQUESTED',
} as const; }
export type LogEntryStatus = keyof typeof LogEntryStatuses;