336 lines
11 KiB
TypeScript
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;
|
|
}
|
|
}
|