red-ui/apps/red-ui/src/app/models/file/annotation.wrapper.ts
2023-12-13 13:54:21 +02:00

336 lines
11 KiB
TypeScript

import { marker as _ } from '@biesbjerg/ngx-translate-extract-marker';
import { IListable } from '@iqser/common-ui';
import {
annotationDefaultColorConfig,
annotationEntityColorConfig,
AnnotationIconType,
ChangeType,
DefaultColors,
Dictionary,
Earmark,
EntityTypes,
EntryStates,
FalsePositiveSuperTypes,
IEntityLogEntry,
ILegalBasis,
IPoint,
IRectangle,
LogEntryEngine,
LogEntryEngines,
LowLevelFilterTypes,
ManualRedactionTypes,
SuperType,
SuperTypeMapper,
SuperTypes,
} from '@red/domain';
import { annotationTypesTranslations } from '@translations/annotation-types-translations';
export class AnnotationWrapper implements IListable {
id: string;
superType: SuperType;
superTypeLabel: string;
type: string;
typeLabel?: string;
color: string;
numberOfComments = 0;
firstTopLeftPoint: IPoint;
shortContent: string;
content: string;
value: string;
pageNumber: number;
dictionaryOperation = false;
positions: IRectangle[] = [];
legalBasisValue: string;
// AREA === rectangle
AREA = false;
HINT = false;
IMAGE = false;
IMAGE_HINT = false;
section?: string;
reference: string[] = [];
imported = false;
manual = false;
pending = false;
textAfter?: string;
textBefore?: string;
isChangeLogEntry = false;
engines: LogEntryEngine[] = [];
hasBeenResized: boolean;
hasBeenRecategorized: boolean;
hasLegalBasisChanged: boolean;
hasBeenForcedHint: boolean;
hasBeenForcedRedaction: boolean;
hasBeenRemovedByManualOverride: boolean;
isRemoved = false;
isRemovedLocally = false;
get isRuleBased() {
return this.engines.includes(LogEntryEngines.RULE);
}
get searchKey(): string {
return this.id;
}
get descriptor() {
return this.isModifyDictionary ? _('dictionary') : _('type');
}
get hasTextAfter() {
return this.textAfter && this.textAfter.trim().length > 0;
}
get hasTextBefore() {
return this.textBefore && this.textBefore.trim().length > 0;
}
get canBeMarkedAsFalsePositive() {
return (
(this.isRecommendation || this.superType === SuperTypes.Redaction) &&
!this.isImage &&
!this.imported &&
!this.pending &&
!this.hasBeenResized
);
}
get isSkipped() {
return this.superType === SuperTypes.Skipped;
}
get isImage() {
return this.type?.toLowerCase() === 'image' || this.IMAGE || this.IMAGE_HINT;
}
get isOCR() {
return this.type?.toLowerCase() === 'ocr';
}
get topLevelFilter() {
return !LowLevelFilterTypes[this.superType];
}
get filterKey() {
if (this.isEarmark) {
return this.color;
}
return this.topLevelFilter ? this.superType : this.superType + this.type;
}
get isFalsePositive() {
return this.type?.toLowerCase() === 'false_positive' && !!FalsePositiveSuperTypes[this.superType];
}
get isHint() {
return this.superType === SuperTypes.Hint;
}
get isEarmark() {
return this.superType === SuperTypes.TextHighlight;
}
get iconShape(): AnnotationIconType {
if (this.isRecommendation) {
return 'hexagon';
}
if (this.HINT) {
return 'circle';
}
return 'square';
}
get isDictBasedHint() {
return this.isHint && this.engines.includes(LogEntryEngines.DICTIONARY);
}
get isIgnoredHint() {
return this.superType === SuperTypes.IgnoredHint;
}
get isRedacted() {
return this.superType === SuperTypes.Redaction || this.superType === SuperTypes.ManualRedaction;
}
get isModifyDictionary() {
return this.dictionaryOperation;
}
get hasRedactionChanges(): boolean {
return (
this.hasBeenResized ||
this.hasBeenRecategorized ||
this.hasLegalBasisChanged ||
this.hasBeenForcedHint ||
this.hasBeenForcedRedaction ||
this.hasBeenRemovedByManualOverride
);
}
get isRecommendation() {
return this.superType === SuperTypes.Recommendation;
}
get x() {
return this.firstTopLeftPoint.x;
}
get y() {
return this.firstTopLeftPoint.y;
}
get legalBasis() {
return this.legalBasisValue;
}
get width(): number {
return Math.floor(this.positions[0].width);
}
get height(): number {
return Math.floor(this.positions[0].height);
}
static fromEarmark(earmark: Earmark) {
const annotationWrapper = new AnnotationWrapper();
annotationWrapper.id = earmark.id;
annotationWrapper.pageNumber = earmark.positions[0].page;
annotationWrapper.superType = SuperTypes.TextHighlight;
annotationWrapper.type = SuperTypes.TextHighlight;
annotationWrapper.value = 'Imported';
annotationWrapper.color = earmark.hexColor;
annotationWrapper.positions = earmark.positions;
annotationWrapper.firstTopLeftPoint = earmark.positions[0]?.topLeft;
annotationWrapper.superTypeLabel = annotationTypesTranslations[annotationWrapper.superType];
return annotationWrapper;
}
static fromData(
logEntry: IEntityLogEntry,
dictionary: Dictionary,
changeLogType: ChangeType,
legalBasisList: ILegalBasis[],
isDocumine: boolean,
defaultColors: DefaultColors,
) {
const annotationWrapper = new AnnotationWrapper();
annotationWrapper.id = logEntry.id;
annotationWrapper.isChangeLogEntry = logEntry.state === EntryStates.REMOVED || !!changeLogType;
annotationWrapper.type = logEntry.type;
annotationWrapper.value = logEntry.value;
annotationWrapper.firstTopLeftPoint = { x: logEntry.positions[0].rectangle[0], y: logEntry.positions[0].rectangle[1] };
annotationWrapper.pageNumber = logEntry.positions[0].pageNumber;
annotationWrapper.positions = logEntry.positions.map(p => ({
page: p.pageNumber,
height: p.rectangle[3],
width: p.rectangle[2],
topLeft: { x: p.rectangle[0], y: p.rectangle[1] },
}));
annotationWrapper.textBefore = logEntry.textBefore;
annotationWrapper.textAfter = logEntry.textAfter;
annotationWrapper.dictionaryOperation = logEntry.dictionaryEntry;
annotationWrapper.HINT = logEntry.entryType === EntityTypes.HINT;
annotationWrapper.IMAGE = logEntry.entryType === EntityTypes.IMAGE;
annotationWrapper.AREA = logEntry.entryType === EntityTypes.AREA;
annotationWrapper.IMAGE_HINT = logEntry.entryType === EntityTypes.IMAGE_HINT;
annotationWrapper.numberOfComments = logEntry.numberOfComments;
annotationWrapper.imported = logEntry.imported;
annotationWrapper.legalBasisValue = logEntry.legalBasis;
annotationWrapper.manual = logEntry.manualChanges?.length > 0;
annotationWrapper.engines = logEntry.engines ?? [];
annotationWrapper.section = logEntry.section;
annotationWrapper.reference = logEntry.reference || [];
annotationWrapper.hasBeenResized = !!logEntry.manualChanges?.find(c => c.manualRedactionType === ManualRedactionTypes.RESIZE);
annotationWrapper.hasBeenRecategorized = !!logEntry.manualChanges?.find(
c => c.manualRedactionType === ManualRedactionTypes.RECATEGORIZE,
);
annotationWrapper.hasLegalBasisChanged = !!logEntry.manualChanges?.find(
c => c.manualRedactionType === ManualRedactionTypes.LEGAL_BASIS_CHANGE,
);
annotationWrapper.hasBeenForcedHint = !!logEntry.manualChanges?.find(
c => c.manualRedactionType === ManualRedactionTypes.FORCE_HINT,
);
annotationWrapper.hasBeenForcedRedaction = !!logEntry.manualChanges?.find(
c => c.manualRedactionType === ManualRedactionTypes.FORCE_REDACT,
);
annotationWrapper.hasBeenRemovedByManualOverride = !!logEntry.manualChanges?.find(
c => c.manualRedactionType === ManualRedactionTypes.REMOVE_LOCALLY,
);
const content = this.#createContent(annotationWrapper, logEntry, isDocumine);
annotationWrapper.shortContent = this.#getShortContent(annotationWrapper, legalBasisList) || content;
annotationWrapper.content = content;
const lastRelevantManualChange = logEntry.manualChanges?.at(-1);
annotationWrapper.pending = lastRelevantManualChange && !lastRelevantManualChange.processed;
annotationWrapper.superType = SuperTypeMapper[logEntry.entryType][logEntry.state](logEntry);
annotationWrapper.superTypeLabel = annotationTypesTranslations[annotationWrapper.superType];
annotationWrapper.isRemoved = logEntry.state === EntryStates.REMOVED;
annotationWrapper.isRemovedLocally = lastRelevantManualChange?.manualRedactionType === ManualRedactionTypes.REMOVE_LOCALLY;
annotationWrapper.typeLabel = dictionary?.virtual ? undefined : dictionary?.label;
const colorKey = annotationEntityColorConfig[annotationWrapper.superType];
const defaultColor = annotationDefaultColorConfig[annotationWrapper.superType];
annotationWrapper.color = dictionary ? (dictionary[colorKey] as string) : (defaultColors[defaultColor] as string);
annotationWrapper['entry'] = logEntry;
return annotationWrapper;
}
static #createContent(annotationWrapper: AnnotationWrapper, logEntry: IEntityLogEntry, isDocumine: boolean) {
let content = '';
if (logEntry.matchedRule) {
content += `Rule ${logEntry.matchedRule} matched${isDocumine ? ':' : ''} \n\n`;
}
if (logEntry.reason) {
if (isDocumine && logEntry.reason.slice(-1) === '.') {
logEntry.reason = logEntry.reason.slice(0, -1);
}
content += logEntry.reason + '\n\n';
//remove leading and trailing commas and whitespaces
content = content.replace(/(^[, ]*)|([, ]*$)/g, '');
content = content.substring(0, 1).toUpperCase() + content.substring(1);
}
if (annotationWrapper.legalBasis && !isDocumine) {
content += 'Legal basis: ' + annotationWrapper.legalBasis + '\n\n';
}
if (annotationWrapper.hasBeenRemovedByManualOverride) {
content += 'Removed by manual override';
}
if (logEntry.section) {
let prefix = `In section${isDocumine ? '' : ':'} `;
if (content.length) {
prefix = ` ${prefix.toLowerCase()}`;
}
content += `${prefix} "${logEntry.section}"`;
}
return content;
}
static #getShortContent(annotationWrapper: AnnotationWrapper, legalBasisList: ILegalBasis[]) {
if (annotationWrapper.legalBasis) {
const lb = legalBasisList.find(lbm => lbm.reason?.toLowerCase().includes(annotationWrapper.legalBasis.toLowerCase()));
if (lb) {
return lb.name;
}
}
return annotationWrapper.legalBasis;
}
}