diff --git a/apps/red-ui/src/app/models/file/annotation.wrapper.ts b/apps/red-ui/src/app/models/file/annotation.wrapper.ts index 6751a0812..79f0f84a7 100644 --- a/apps/red-ui/src/app/models/file/annotation.wrapper.ts +++ b/apps/red-ui/src/app/models/file/annotation.wrapper.ts @@ -1,13 +1,14 @@ -import { RedactionLogEntryWrapper } from './redaction-log-entry.wrapper'; import { annotationTypesTranslations } from '../../translations/annotation-types-translations'; 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 = | 'suggestion-change-legal-basis' | 'suggestion-recategorize-image' | 'suggestion-add-dictionary' | 'suggestion-force-redaction' + | 'suggestion-force-hint' | 'suggestion-resize' | 'suggestion-remove-dictionary' | 'suggestion-add' @@ -44,15 +45,14 @@ export class AnnotationWrapper { legalBasisChangeValue?: string; resizing?: boolean; rectangle?: boolean; - hintDictionary?: boolean; section?: string; reference: Array; - manual?: boolean; - image?: boolean; + manual?: boolean; hidden?: boolean; - force?: boolean; + pending?: boolean; + hintDictionary?: boolean; textAfter?: string; textBefore?: string; @@ -64,11 +64,10 @@ export class AnnotationWrapper { hasBeenResized: boolean; hasBeenRecategorized: boolean; hasLegalBasisChanged: boolean; - hasBeenForced: boolean; + hasBeenForcedHint: boolean; + hasBeenForcedRedaction: boolean; hasBeenRemovedByManualOverride: boolean; - private _origin: RedactionLogEntryWrapper; - get isChangeLogRemoved() { return this.changeLogType === 'REMOVED'; } @@ -204,7 +203,8 @@ export class AnnotationWrapper { this.hasBeenResized || this.hasBeenRecategorized || this.hasLegalBasisChanged || - this.hasBeenForced || + this.hasBeenForcedHint || + this.hasBeenForcedRedaction || this.hasBeenRemovedByManualOverride ); } @@ -233,41 +233,48 @@ export class AnnotationWrapper { return this.legalBasisChangeValue || this.legalBasisValue; } - static fromData(redactionLogEntry?: RedactionLogEntryWrapper) { + static fromData(redactionLogEntry?: RedactionLogEntry) { const annotationWrapper = new AnnotationWrapper(); - annotationWrapper._origin = redactionLogEntry; - annotationWrapper.annotationId = redactionLogEntry.id; annotationWrapper.isChangeLogEntry = redactionLogEntry.isChangeLogEntry; annotationWrapper.changeLogType = redactionLogEntry.changeLogType; annotationWrapper.redaction = redactionLogEntry.redacted; annotationWrapper.hint = redactionLogEntry.hint; annotationWrapper.typeValue = redactionLogEntry.type; - annotationWrapper.recategorizationType = redactionLogEntry.recategorizationType; annotationWrapper.value = redactionLogEntry.value; annotationWrapper.firstTopLeftPoint = redactionLogEntry.positions[0]?.topLeft; annotationWrapper.pageNumber = redactionLogEntry.positions[0]?.page; annotationWrapper.positions = redactionLogEntry.positions; - annotationWrapper.status = redactionLogEntry.status; annotationWrapper.textBefore = redactionLogEntry.textBefore; annotationWrapper.textAfter = redactionLogEntry.textAfter; annotationWrapper.dictionaryOperation = redactionLogEntry.dictionaryEntry; annotationWrapper.image = redactionLogEntry.image; - annotationWrapper.legalBasisChangeValue = redactionLogEntry.legalBasisChangeValue; annotationWrapper.legalBasisValue = redactionLogEntry.legalBasis; annotationWrapper.comments = redactionLogEntry.comments || []; - annotationWrapper.manual = redactionLogEntry.manual; + annotationWrapper.manual = redactionLogEntry.manualChanges.length > 0; annotationWrapper.engines = redactionLogEntry.engines; annotationWrapper.section = redactionLogEntry.section; annotationWrapper.reference = redactionLogEntry.reference || []; annotationWrapper.rectangle = redactionLogEntry.rectangle; - annotationWrapper.hasBeenResized = redactionLogEntry.hasBeenResized; - annotationWrapper.hasBeenRecategorized = redactionLogEntry.hasBeenRecategorized; - annotationWrapper.hasLegalBasisChanged = redactionLogEntry.hasLegalBasisChanged; - annotationWrapper.hasBeenForced = redactionLogEntry.hasBeenForced; - annotationWrapper.hasBeenRemovedByManualOverride = redactionLogEntry.hasBeenRemovedByManualOverride; - annotationWrapper.hintDictionary = redactionLogEntry.hintDictionary; + annotationWrapper.hasBeenResized = !!redactionLogEntry.manualChanges.find( + c => c.manualRedactionType === ManualRedactionType.RESIZE && c.annotationStatus === LogEntryStatus.APPROVED, + ); + annotationWrapper.hasBeenRecategorized = !!redactionLogEntry.manualChanges.find( + c => c.manualRedactionType === ManualRedactionType.RECATEGORIZE && c.annotationStatus === LogEntryStatus.APPROVED, + ); + 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._setSuperType(annotationWrapper, redactionLogEntry); @@ -277,167 +284,38 @@ export class AnnotationWrapper { return annotationWrapper; } - private static _handleRecommendations(annotationWrapper: AnnotationWrapper, redactionLogEntry: RedactionLogEntryWrapper) { + private static _handleRecommendations(annotationWrapper: AnnotationWrapper, redactionLogEntry: RedactionLogEntry) { if (annotationWrapper.superType === 'recommendation') { annotationWrapper.recommendationType = redactionLogEntry.type.substr('recommendation_'.length); } } - private static _setSuperType(annotationWrapper: AnnotationWrapper, redactionLogEntryWrapper: RedactionLogEntryWrapper) { + private static _setSuperType(annotationWrapper: AnnotationWrapper, redactionLogEntryWrapper: RedactionLogEntry) { if (redactionLogEntryWrapper.recommendation) { annotationWrapper.superType = 'recommendation'; return; } - if (redactionLogEntryWrapper.manualRedactionType === 'RESIZE') { - if (redactionLogEntryWrapper.status === 'REQUESTED') { - annotationWrapper.superType = 'suggestion-resize'; - return; - } - } + if (redactionLogEntryWrapper.manualChanges.length) { + const lastManualChange = redactionLogEntryWrapper.manualChanges[redactionLogEntryWrapper.manualChanges.length - 1]; - if (redactionLogEntryWrapper.manualRedactionType === 'FORCE_REDACT') { - annotationWrapper.force = true; + annotationWrapper.pending = !lastManualChange.processed; - if (redactionLogEntryWrapper.status === 'REQUESTED') { - annotationWrapper.superType = 'suggestion-force-redaction'; - } else if (redactionLogEntryWrapper.status === 'APPROVED') { - annotationWrapper.superType = redactionLogEntryWrapper.hint ? 'hint' : 'redaction'; - } else { - 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 = AnnotationWrapper._selectSuperType(redactionLogEntryWrapper, lastManualChange); + } else { + if (redactionLogEntryWrapper.recommendation) { + annotationWrapper.superType = 'recommendation'; + } else if (redactionLogEntryWrapper.redacted) { annotationWrapper.superType = 'redaction'; - } - return; - } - - 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 if (redactionLogEntryWrapper.hint) { + annotationWrapper.superType = 'hint'; } else { - 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.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'; + annotationWrapper.superType = 'skipped'; } } } - private static _createContent(annotationWrapper: AnnotationWrapper, entry: RedactionLogEntryWrapper) { + private static _createContent(annotationWrapper: AnnotationWrapper, entry: RedactionLogEntry) { let content = ''; if (entry.matchedRule) { content += `Rule ${entry.matchedRule} matched \n\n`; @@ -459,7 +337,7 @@ export class AnnotationWrapper { annotationWrapper.content = content; } - private static _getShortContent(annotationWrapper: AnnotationWrapper, entry: RedactionLogEntryWrapper) { + private static _getShortContent(annotationWrapper: AnnotationWrapper, entry: RedactionLogEntry) { if (annotationWrapper.legalBasis) { const lb = entry.legalBasisList?.find(lbm => lbm.reason.toLowerCase().includes(annotationWrapper.legalBasis.toLowerCase())); if (lb) { @@ -469,4 +347,121 @@ export class AnnotationWrapper { 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; + } + } } diff --git a/apps/red-ui/src/app/models/file/file-data.model.ts b/apps/red-ui/src/app/models/file/file-data.model.ts index 16727d14b..3640fc3ae 100644 --- a/apps/red-ui/src/app/models/file/file-data.model.ts +++ b/apps/red-ui/src/app/models/file/file-data.model.ts @@ -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 { RedactionLogEntryWrapper } from './redaction-log-entry.wrapper'; import * as moment from 'moment'; import { BehaviorSubject } from 'rxjs'; +import { RedactionLogEntry } from './redaction-log.entry'; export class FileDataModel { static readonly DELTA_VIEW_TIME = 10 * 60 * 1000; // 10 minutes; @@ -54,7 +64,7 @@ export class FileDataModel { } private _buildAllAnnotations() { - const entries: RedactionLogEntryWrapper[] = this._convertData(); + const entries: RedactionLogEntry[] = this._convertData(); const previousAnnotations = this.allAnnotations || []; this.allAnnotations = entries @@ -77,31 +87,30 @@ export class FileDataModel { }); } - private _convertData(): RedactionLogEntryWrapper[] { - let result: RedactionLogEntryWrapper[] = []; + private _convertData(): RedactionLogEntry[] { + let result: RedactionLogEntry[] = []; - const reasonAnnotationIds: { [key: string]: RedactionLogEntryWrapper[] } = {}; + const reasonAnnotationIds: { [key: string]: RedactionLogEntry[] } = {}; this.redactionLog.redactionLogEntry?.forEach(redactionLogEntry => { // 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 ( - redactionLogEntryWrapper.status === 'DECLINED' && - redactionLogEntryWrapper.manualRedactionType === 'ADD' && - redactionLogEntryWrapper.type === 'false_positive' + redactionLogEntry.manualChanges.find( + mc => + 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, // check reason if (!reasonAnnotationIds[redactionLogEntry.reason]) { @@ -123,16 +132,16 @@ export class FileDataModel { return !matched; }); - result.forEach(redactionLogEntry => { - redactionLogEntry.legalBasisList = this.redactionLog.legalBasis; - }); - result = result.filter(r => !r.hidden); return result; } - private _isChangeLogEntry(redactionLogEntry: IRedactionLogEntry, wrapper: RedactionLogEntryWrapper) { + private getChangeLogValues(redactionLogEntry: IRedactionLogEntry): { + hidden: boolean; + changeLogType: ChangeType; + isChangeLogEntry: boolean; + } { if (this._file.numberOfAnalyses > 1) { const viableChanges = redactionLogEntry.changes.filter(c => c.analysisNumber > 1); viableChanges.sort((a, b) => moment(a.dateTime).valueOf() - moment(b.dateTime).valueOf()); @@ -150,19 +159,28 @@ export class FileDataModel { // at least one unseen change if (relevantChanges.length > 0) { // 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(); this.hasChangeLog$.next(true); + return { + changeLogType: relevantChanges[relevantChanges.length - 1].type, + isChangeLogEntry: true, + hidden: false, + }; } else { // no relevant changes - hide removed anyway - wrapper.isChangeLogEntry = false; - wrapper.hidden = lastChange && lastChange.type === 'REMOVED'; + return { + changeLogType: null, + isChangeLogEntry: false, + hidden: lastChange && lastChange.type === 'REMOVED', + }; } } else { // Page doesn't have a view-time - wrapper.isChangeLogEntry = false; - wrapper.hidden = lastChange && lastChange.type === 'REMOVED'; + return { + changeLogType: null, + isChangeLogEntry: false, + hidden: lastChange && lastChange.type === 'REMOVED', + }; } } // console.log(wrapper.changeLogType, wrapper.hidden, wrapper.isChangeLogEntry, wrapper.value, lastChange); diff --git a/apps/red-ui/src/app/models/file/redaction-log-entry.wrapper.ts b/apps/red-ui/src/app/models/file/redaction-log-entry.wrapper.ts deleted file mode 100644 index a406aba68..000000000 --- a/apps/red-ui/src/app/models/file/redaction-log-entry.wrapper.ts +++ /dev/null @@ -1,56 +0,0 @@ -import { IChange, IComment, ILegalBasis, IRectangle } from '@red/domain'; - -export interface RedactionLogEntryWrapper { - changes?: Array; - dossierDictionaryEntry?: boolean; - endOffset?: number; - excluded?: boolean; - imageHasTransparency?: boolean; - manualRedactionUserId?: string; - reference?: Array; - startOffset?: number; - type?: string; - rectangle?: boolean; - hintDictionary?: boolean; - - color?: Array; - dictionaryEntry?: boolean; - hint?: boolean; - id?: string; - legalBasis?: string; - manual?: boolean; - manualRedactionType?: 'ADD' | 'REMOVE' | 'LEGAL_BASIS_CHANGE' | 'FORCE_REDACT' | 'RECATEGORIZE' | 'RESIZE'; - matchedRule?: number; - positions?: Array; - 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; -} diff --git a/apps/red-ui/src/app/models/file/redaction-log.entry.ts b/apps/red-ui/src/app/models/file/redaction-log.entry.ts new file mode 100644 index 000000000..e53e6e570 --- /dev/null +++ b/apps/red-ui/src/app/models/file/redaction-log.entry.ts @@ -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; + } +} diff --git a/apps/red-ui/src/app/translations/annotation-changes-translations.ts b/apps/red-ui/src/app/translations/annotation-changes-translations.ts index 7bd481fa1..4cfb7801c 100644 --- a/apps/red-ui/src/app/translations/annotation-changes-translations.ts +++ b/apps/red-ui/src/app/translations/annotation-changes-translations.ts @@ -6,6 +6,7 @@ export const annotationChangesTranslations: { [key in KeysOf] hasBeenResized: _('annotation-changes.resized'), hasBeenRecategorized: _('annotation-changes.recategorized'), hasLegalBasisChanged: _('annotation-changes.legal-basis'), - hasBeenForced: _('annotation-changes.forced'), + hasBeenForcedRedaction: _('annotation-changes.forced-redaction'), + hasBeenForcedHint: _('annotation-changes.forced-hint'), hasBeenRemovedByManualOverride: _('annotation-changes.removed-manual'), } as const; diff --git a/apps/red-ui/src/app/translations/annotation-types-translations.ts b/apps/red-ui/src/app/translations/annotation-types-translations.ts index c8113674f..65ad140ef 100644 --- a/apps/red-ui/src/app/translations/annotation-types-translations.ts +++ b/apps/red-ui/src/app/translations/annotation-types-translations.ts @@ -14,6 +14,7 @@ export const annotationTypesTranslations: { [key in AnnotationSuperType]: string 'suggestion-change-legal-basis': _('annotation-type.suggestion-change-legal-basis'), 'suggestion-recategorize-image': _('annotation-type.suggestion-recategorize-image'), 'suggestion-force-redaction': _('annotation-type.suggestion-force-redaction'), + 'suggestion-force-hint': _('annotation-type.suggestion-force-hint'), 'suggestion-remove': _('annotation-type.suggestion-remove'), 'suggestion-remove-dictionary': _('annotation-type.suggestion-remove-dictionary'), 'suggestion-resize': _('annotation-type.suggestion-resize'), diff --git a/apps/red-ui/src/app/utils/sorters/super-type-sorter.ts b/apps/red-ui/src/app/utils/sorters/super-type-sorter.ts index 4c04efa20..644827a55 100644 --- a/apps/red-ui/src/app/utils/sorters/super-type-sorter.ts +++ b/apps/red-ui/src/app/utils/sorters/super-type-sorter.ts @@ -3,7 +3,8 @@ import { AnnotationSuperType } from '../../models/file/annotation.wrapper'; export const SuperTypeSorter: { [key in AnnotationSuperType]: number } = { 'suggestion-change-legal-basis': 14, 'suggestion-force-redaction': 15, - 'suggestion-recategorize-image': 16, + 'suggestion-force-hint': 16, + 'suggestion-recategorize-image': 17, 'suggestion-resize': 18, recommendation: 7, 'suggestion-add-dictionary': 12, @@ -14,6 +15,6 @@ export const SuperTypeSorter: { [key in AnnotationSuperType]: number } = { skipped: 50, redaction: 1, 'manual-redaction': 2, - hint: 17, + hint: 19, 'declined-suggestion': 20, }; diff --git a/apps/red-ui/src/assets/i18n/en.json b/apps/red-ui/src/assets/i18n/en.json index b17b42042..82df9e7b9 100644 --- a/apps/red-ui/src/assets/i18n/en.json +++ b/apps/red-ui/src/assets/i18n/en.json @@ -272,7 +272,8 @@ "undo": "Undo" }, "annotation-changes": { - "forced": "Redaction forced", + "forced-redaction": "Redaction forced", + "forced-hint": "Hint forced", "header": "Manual changes:", "legal-basis": "Reason changed", "recategorized": "Image category changed", @@ -296,8 +297,9 @@ "suggestion-add-dictionary": "Suggested dictionary add", "suggestion-change-legal-basis": "Suggested change legal basis", "suggestion-force-redaction": "Suggestion force redaction", + "suggestion-force-redaction": "Suggestion force hint", "suggestion-recategorize-image": "Suggested recategorize image", - "suggestion-remove": "Suggested hint removal", + "suggestion-remove": "Suggested local removal", "suggestion-remove-dictionary": "Suggested dictionary removal", "suggestion-resize": "Suggested Resize" }, diff --git a/libs/common-ui b/libs/common-ui index 6c79b02a5..31b60ff11 160000 --- a/libs/common-ui +++ b/libs/common-ui @@ -1 +1 @@ -Subproject commit 6c79b02a50f268a6b663e8e46d88f3c0d333a608 +Subproject commit 31b60ff117a8d8ecb9617be3b0c9e1822d437034 diff --git a/libs/red-domain/src/lib/redaction-log/change.ts b/libs/red-domain/src/lib/redaction-log/change.ts index 27b3ee0f4..b0cf47a38 100644 --- a/libs/red-domain/src/lib/redaction-log/change.ts +++ b/libs/red-domain/src/lib/redaction-log/change.ts @@ -4,9 +4,8 @@ export interface IChange { type?: ChangeType; } -export const ChangeTypes = { - ADDED: 'ADDED', - CHANGED: 'CHANGED', - REMOVED: 'REMOVED', -} as const; -export type ChangeType = keyof typeof ChangeTypes; +export enum ChangeType { + ADDED = 'ADDED', + CHANGED = 'CHANGED', + REMOVED = 'REMOVED', +} diff --git a/libs/red-domain/src/lib/redaction-log/index.ts b/libs/red-domain/src/lib/redaction-log/index.ts index e2abbc277..04deded25 100644 --- a/libs/red-domain/src/lib/redaction-log/index.ts +++ b/libs/red-domain/src/lib/redaction-log/index.ts @@ -10,3 +10,4 @@ export * from './manual-add.response'; export * from './approve-request'; export * from './image-recategorization.request'; export * from './resize.request'; +export * from './manual-change'; diff --git a/libs/red-domain/src/lib/redaction-log/manual-change.ts b/libs/red-domain/src/lib/redaction-log/manual-change.ts new file mode 100644 index 000000000..923a39d5d --- /dev/null +++ b/libs/red-domain/src/lib/redaction-log/manual-change.ts @@ -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; +} diff --git a/libs/red-domain/src/lib/redaction-log/redaction-log-entry.ts b/libs/red-domain/src/lib/redaction-log/redaction-log-entry.ts index c2577442e..7317ef109 100644 --- a/libs/red-domain/src/lib/redaction-log/redaction-log-entry.ts +++ b/libs/red-domain/src/lib/redaction-log/redaction-log-entry.ts @@ -1,17 +1,18 @@ import { IChange } from './change'; import { IComment } from './comment'; import { IRectangle } from '../geometry'; -import { LogEntryEngine, LogEntryStatus, ManualRedactionType } from './types'; -import { List } from '@iqser/common-ui'; +import { LogEntryEngine } from './types'; +import { IManualChange } from './manual-change'; export interface IRedactionLogEntry { changes?: IChange[]; - color?: List; - comments?: List; + manualChanges?: IManualChange[]; + color?: number[]; + comments?: IComment[]; dictionaryEntry?: boolean; dossierDictionaryEntry?: boolean; endOffset?: number; - engines?: List; + engines?: LogEntryEngine[]; excluded?: boolean; hint?: boolean; rectangle?: boolean; @@ -19,21 +20,15 @@ export interface IRedactionLogEntry { image?: boolean; imageHasTransparency?: boolean; legalBasis?: string; - legalBasisChangeValue?: string; - manual?: boolean; - manualRedactionType?: ManualRedactionType; - manualRedactionUserId?: string; matchedRule?: number; - positions?: List; + positions?: IRectangle[]; reason?: string; - recategorizationType?: string; recommendation?: boolean; redacted?: boolean; - reference?: List; + reference?: string[]; section?: string; sectionNumber?: number; startOffset?: number; - status?: LogEntryStatus; textAfter?: string; textBefore?: string; type?: string; diff --git a/libs/red-domain/src/lib/redaction-log/types.ts b/libs/red-domain/src/lib/redaction-log/types.ts index 1c7696051..6f8477830 100644 --- a/libs/red-domain/src/lib/redaction-log/types.ts +++ b/libs/red-domain/src/lib/redaction-log/types.ts @@ -1,23 +1,23 @@ -export const LogEntryEngines = { - DICTIONARY: 'DICTIONARY', - NER: 'NER', - RULE: 'RULE', -}; -export type LogEntryEngine = keyof typeof LogEntryEngines; +export enum LogEntryEngine { + DICTIONARY = 'DICTIONARY', + NER = 'NER', + RULE = 'RULE', +} -export const ManualRedactionTypes = { - ADD: 'ADD', - FORCE_REDACT: 'FORCE_REDACT', - LEGAL_BASIS_CHANGE: 'LEGAL_BASIS_CHANGE', - RECATEGORIZE: 'RECATEGORIZE', - REMOVE: 'REMOVE', -} as const; -export type ManualRedactionType = keyof typeof ManualRedactionTypes; +export enum ManualRedactionType { + ADD_LOCALLY = 'ADD_LOCALLY', + ADD_TO_DICTIONARY = 'ADD_TO_DICTIONARY', + REMOVE_LOCALLY = 'REMOVE_LOCALLY', + REMOVE_FROM_DICTIONARY = 'REMOVE_FROM_DICTIONARY', + FORCE_REDACT = 'FORCE_REDACT', + FORCE_HINT = 'FORCE_HINT', + RECATEGORIZE = 'RECATEGORIZE', + LEGAL_BASIS_CHANGE = 'LEGAL_BASIS_CHANGE', + RESIZE = 'RESIZE', +} -export const LogEntryStatuses = { - APPROVED: 'APPROVED', - DECLINED: 'DECLINED', - REQUESTED: 'REQUESTED', -} as const; - -export type LogEntryStatus = keyof typeof LogEntryStatuses; +export enum LogEntryStatus { + APPROVED = 'APPROVED', + DECLINED = 'DECLINED', + REQUESTED = 'REQUESTED', +}