RED-7619 update annotation wrapper

This commit is contained in:
Dan Percic 2023-10-30 15:02:20 +02:00
parent ffa9a5830f
commit 7d141c5b23
8 changed files with 85 additions and 80 deletions

View File

@ -32,7 +32,7 @@ export const canChangeLegalBasis = (annotation: AnnotationWrapper, canAddRedacti
canAddRedaction && annotation.isRedacted && !annotation.pending;
export const canRecategorizeAnnotation = (annotation: AnnotationWrapper, canRecategorize: boolean) =>
canRecategorize && (annotation.isImage || annotation.HINT) && !annotation.pending;
canRecategorize && (annotation.isImage || annotation.isDictBasedHint) && !annotation.pending;
export const canResizeAnnotation = (annotation: AnnotationWrapper, canAddRedaction: boolean) =>
canAddRedaction &&

View File

@ -186,10 +186,6 @@ export class AnnotationWrapper implements IListable {
return Math.floor(this.positions[0].height);
}
get previewAnnotation() {
return this.isRedacted;
}
static fromEarmark(earmark: Earmark) {
const annotationWrapper = new AnnotationWrapper();
@ -244,27 +240,30 @@ export class AnnotationWrapper implements IListable {
annotationWrapper.engines = logEntry.engines ?? [];
annotationWrapper.section = logEntry.section;
annotationWrapper.reference = logEntry.reference || [];
annotationWrapper.hasBeenResized = !!logEntry.manualChanges?.find(
c => c.manualRedactionType === ManualRedactionTypes.RESIZE && c.processed,
);
annotationWrapper.hasBeenResized = !!logEntry.manualChanges?.find(c => c.manualRedactionType === ManualRedactionTypes.RESIZE);
annotationWrapper.hasBeenRecategorized = !!logEntry.manualChanges?.find(
c => c.manualRedactionType === ManualRedactionTypes.RECATEGORIZE && c.processed,
c => c.manualRedactionType === ManualRedactionTypes.RECATEGORIZE,
);
annotationWrapper.hasLegalBasisChanged = !!logEntry.manualChanges?.find(
c => c.manualRedactionType === ManualRedactionTypes.LEGAL_BASIS_CHANGE && c.processed,
c => c.manualRedactionType === ManualRedactionTypes.LEGAL_BASIS_CHANGE,
);
annotationWrapper.hasBeenForcedHint = !!logEntry.manualChanges?.find(
c => c.manualRedactionType === ManualRedactionTypes.FORCE_HINT && c.processed,
c => c.manualRedactionType === ManualRedactionTypes.FORCE_HINT,
);
annotationWrapper.hasBeenForcedRedaction = !!logEntry.manualChanges?.find(
c => c.manualRedactionType === ManualRedactionTypes.FORCE_REDACT && c.processed,
c => c.manualRedactionType === ManualRedactionTypes.FORCE_REDACT,
);
annotationWrapper.hasBeenRemovedByManualOverride = !!logEntry.manualChanges?.find(
c => c.manualRedactionType === ManualRedactionTypes.REMOVE_LOCALLY && c.processed,
c => c.manualRedactionType === ManualRedactionTypes.REMOVE_LOCALLY,
);
this.#createContent(annotationWrapper, logEntry, isDocumine, legalBasisList);
this.#setSuperType(annotationWrapper, logEntry);
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.typeLabel = dictionary.virtual ? undefined : dictionary.label;
@ -276,18 +275,7 @@ export class AnnotationWrapper implements IListable {
return annotationWrapper;
}
static #setSuperType(annotationWrapper: AnnotationWrapper, logEntry: IEntityLogEntry) {
const lastRelevantManualChange = logEntry.manualChanges?.at(-1);
annotationWrapper.pending = lastRelevantManualChange && !lastRelevantManualChange.processed;
annotationWrapper.superType = SuperTypeMapper[logEntry.entryType][logEntry.state](logEntry);
}
static #createContent(
annotationWrapper: AnnotationWrapper,
logEntry: IEntityLogEntry,
isDocumine: boolean,
legalBasisList: ILegalBasis[],
) {
static #createContent(annotationWrapper: AnnotationWrapper, logEntry: IEntityLogEntry, isDocumine: boolean) {
let content = '';
if (logEntry.matchedRule) {
content += `Rule ${logEntry.matchedRule} matched${isDocumine ? ':' : ''} \n\n`;
@ -320,8 +308,7 @@ export class AnnotationWrapper implements IListable {
content += `${prefix} "${logEntry.section}"`;
}
annotationWrapper.shortContent = this.#getShortContent(annotationWrapper, legalBasisList) || content;
annotationWrapper.content = content;
return content;
}
static #getShortContent(annotationWrapper: AnnotationWrapper, legalBasisList: ILegalBasis[]) {

View File

@ -1,9 +1,20 @@
import { effect, Injectable, Signal, signal } from '@angular/core';
import { effect, inject, Injectable, Signal, signal } from '@angular/core';
import { toObservable } from '@angular/core/rxjs-interop';
import { marker as _ } from '@biesbjerg/ngx-translate-extract-marker';
import { EntitiesService, getConfig, Toaster } from '@iqser/common-ui';
import { AnnotationWrapper } from '@models/file/annotation.wrapper';
import { ChangeType, ChangeTypes, EntryStates, File, IEntityLog, IEntityLogEntry, ViewedPage, ViewMode, ViewModes } from '@red/domain';
import {
ChangeType,
ChangeTypes,
EntryStates,
File,
IEntityLog,
IEntityLogEntry,
SuperTypeMapper,
ViewedPage,
ViewMode,
ViewModes,
} from '@red/domain';
import { DictionaryService } from '@services/entity-services/dictionary.service';
import { EarmarksService } from '@services/files/earmarks.service';
import { FilesService } from '@services/files/files.service';
@ -35,6 +46,8 @@ export class FileDataService extends EntitiesService<AnnotationWrapper, Annotati
readonly #earmarks = signal<Map<number, AnnotationWrapper[]>>(new Map());
#originalViewedPages: ViewedPage[] = [];
readonly #isDocumine = getConfig().IS_DOCUMINE;
readonly #logger = inject(NGXLogger);
readonly #toaster = inject(Toaster);
protected readonly _entityClass = AnnotationWrapper;
missingTypes = new Set<string>();
readonly earmarks: Signal<Map<number, AnnotationWrapper[]>>;
@ -53,8 +66,6 @@ export class FileDataService extends EntitiesService<AnnotationWrapper, Annotati
private readonly _earmarksService: EarmarksService,
private readonly _multiSelectService: MultiSelectService,
private readonly _filesService: FilesService,
private readonly _toaster: Toaster,
private readonly _logger: NGXLogger,
) {
super();
this.annotations$ = toObservable(this.#annotations);
@ -89,16 +100,16 @@ export class FileDataService extends EntitiesService<AnnotationWrapper, Annotati
async loadAnnotations(file: File) {
if (!file || file.isUnprocessed) {
this._logger.info('[ANNOTATIONS] File is null or unprocessed, skipping annotations loading');
this.#logger.info('[ANNOTATIONS] File is null or unprocessed, skipping annotations loading');
return;
}
this._logger.info('[ANNOTATIONS] Loading annotations...');
this.#logger.info('[ANNOTATIONS] Loading annotations...');
await this.#loadViewedPages(file);
await this.loadRedactionLog();
this._logger.info('[ANNOTATIONS] Annotations loaded');
this.#logger.info('[ANNOTATIONS] Annotations loaded');
}
async annotationsChanged() {
@ -119,10 +130,10 @@ export class FileDataService extends EntitiesService<AnnotationWrapper, Annotati
}
async loadRedactionLog() {
this._logger.info('[REDACTION_LOG] Loading redaction log...');
this.#logger.info('[REDACTION_LOG] Loading redaction log...');
const redactionLog = await this._redactionLogService.getEntityLog(this._state.dossierId, this._state.fileId);
this._logger.info('[REDACTION_LOG] Redaction log loaded', redactionLog);
this.#logger.info('[REDACTION_LOG] Redaction log loaded', redactionLog);
let annotations = await this.#convertData(redactionLog);
this.#checkMissingTypes();
annotations = this._userPreferenceService.isIqserDevMode ? annotations : annotations.filter(a => !a.isFalsePositive);
@ -131,7 +142,7 @@ export class FileDataService extends EntitiesService<AnnotationWrapper, Annotati
#checkMissingTypes() {
if (this.missingTypes.size > 0) {
this._toaster.error(_('error.missing-types'), {
this.#toaster.error(_('error.missing-types'), {
disableTimeOut: true,
params: { missingTypes: Array.from(this.missingTypes).join(', ') },
});
@ -142,12 +153,12 @@ export class FileDataService extends EntitiesService<AnnotationWrapper, Annotati
async #loadViewedPages(file: File) {
if (!this._permissionsService.canMarkPagesAsViewed(file)) {
this._viewedPagesMapService.set(file.fileId, []);
this._logger.info('[VIEWED_PAGES] Cannot mark pages as viewed, skip loading viewed pages');
this.#logger.info('[VIEWED_PAGES] Cannot mark pages as viewed, skip loading viewed pages');
return;
}
this.#originalViewedPages = await this._viewedPagesService.load(file.dossierId, file.fileId);
this._logger.info('[VIEWED_PAGES] Loaded viewed pages', this.#originalViewedPages);
this.#logger.info('[VIEWED_PAGES] Loaded viewed pages', this.#originalViewedPages);
this._viewedPagesMapService.set(file.fileId, this.#originalViewedPages);
}
@ -161,7 +172,7 @@ export class FileDataService extends EntitiesService<AnnotationWrapper, Annotati
return annotation.isChangeLogEntry;
}
return annotation.previewAnnotation;
return annotation.isRedacted;
});
}
@ -186,6 +197,24 @@ export class FileDataService extends EntitiesService<AnnotationWrapper, Annotati
continue;
}
const canBeMappedToASuperType = !!SuperTypeMapper[entry.entryType][entry.state](entry);
if (!canBeMappedToASuperType && this._userPreferenceService.isIqserDevMode) {
this.#logger.error(
`[ENTITY_LOG] Entity ${entry.value} (${entry.entryType}, ${entry.state}) cannot be mapped to a super type!`,
entry,
);
this.#toaster.rawError(
`Skipping entity ${entry.value} (${entry.entryType}, ${entry.state}). It has unexpected state.
Check console for details.`,
{
timeOut: 15000,
easing: 'ease-in-out',
easeTime: 500,
},
);
continue;
}
let dictionary = dictionaries.find(dict => dict.type === entry.type);
if (!dictionary && checkDictionary) {
const dictionaryRequest = this._dictionaryService.loadDictionaryDataForDossierTemplate(this._state.dossierTemplateId);

View File

@ -163,7 +163,7 @@ export class AnnotationDrawService {
(hideSkipped && annotationWrapper.isSkipped) ||
this._annotationManager.isHidden(annotationWrapper.id);
annotation.setCustomData('redact-manager', 'true');
annotation.setCustomData('redaction', String(annotationWrapper.previewAnnotation));
annotation.setCustomData('redaction', String(annotationWrapper.isRedacted));
annotation.setCustomData('skipped', String(annotationWrapper.isSkipped));
annotation.setCustomData('changeLog', String(annotationWrapper.isChangeLogEntry));
annotation.setCustomData('changeLogRemoved', String(annotationWrapper.isIgnored));

View File

@ -14,62 +14,61 @@ export const SuperTypes = {
export type SuperType = ValuesOf<typeof SuperTypes>;
function throwWrongSuperType(entry: IEntityLogEntry): never {
console.log('throwWrongSuperType', entry);
throw new Error(`A ${entry.state} ${entry.entryType} should never be mapped to a super type`);
function wrongSuperTypeHandler(): never | undefined {
return undefined;
}
/**
* https://knecon.atlassian.net/wiki/spaces/RED/pages/102072322/EntityLog+-+Enum+combinations
*/
export const SuperTypeMapper: Record<EntityType, Record<EntryState, (entry: IEntityLogEntry) => SuperType>> = {
export const SuperTypeMapper: Record<EntityType, Record<EntryState, (entry: IEntityLogEntry) => SuperType | undefined>> = {
[EntityTypes.ENTITY]: {
[EntryStates.APPLIED]: entry => (entry.manualChanges.length ? SuperTypes.ManualRedaction : SuperTypes.Redaction),
[EntryStates.SKIPPED]: () => SuperTypes.Skipped,
[EntryStates.IGNORED]: () => SuperTypes.Redaction,
[EntryStates.REMOVED]: throwWrongSuperType,
[EntryStates.REMOVED]: wrongSuperTypeHandler,
},
[EntityTypes.HINT]: {
[EntryStates.APPLIED]: entry => (entry.manualChanges.length ? SuperTypes.ManualHint : SuperTypes.Hint),
[EntryStates.APPLIED]: wrongSuperTypeHandler,
[EntryStates.SKIPPED]: entry => (entry.manualChanges.length ? SuperTypes.ManualHint : SuperTypes.Hint),
[EntryStates.IGNORED]: () => SuperTypes.IgnoredHint,
[EntryStates.REMOVED]: throwWrongSuperType,
[EntryStates.REMOVED]: wrongSuperTypeHandler,
},
[EntityTypes.FALSE_POSITIVE]: {
[EntryStates.APPLIED]: throwWrongSuperType,
[EntryStates.SKIPPED]: throwWrongSuperType,
[EntryStates.IGNORED]: throwWrongSuperType,
[EntryStates.REMOVED]: throwWrongSuperType,
[EntryStates.APPLIED]: wrongSuperTypeHandler,
[EntryStates.SKIPPED]: wrongSuperTypeHandler,
[EntryStates.IGNORED]: wrongSuperTypeHandler,
[EntryStates.REMOVED]: wrongSuperTypeHandler,
},
[EntityTypes.RECOMMENDATION]: {
[EntryStates.APPLIED]: throwWrongSuperType,
[EntryStates.APPLIED]: wrongSuperTypeHandler,
[EntryStates.SKIPPED]: () => SuperTypes.Recommendation,
[EntryStates.IGNORED]: throwWrongSuperType,
[EntryStates.REMOVED]: throwWrongSuperType,
[EntryStates.IGNORED]: wrongSuperTypeHandler,
[EntryStates.REMOVED]: wrongSuperTypeHandler,
},
[EntityTypes.FALSE_RECOMMENDATION]: {
[EntryStates.APPLIED]: throwWrongSuperType,
[EntryStates.SKIPPED]: throwWrongSuperType,
[EntryStates.IGNORED]: throwWrongSuperType,
[EntryStates.REMOVED]: throwWrongSuperType,
[EntryStates.APPLIED]: wrongSuperTypeHandler,
[EntryStates.SKIPPED]: wrongSuperTypeHandler,
[EntryStates.IGNORED]: wrongSuperTypeHandler,
[EntryStates.REMOVED]: wrongSuperTypeHandler,
},
[EntityTypes.AREA]: {
[EntryStates.APPLIED]: () => SuperTypes.Redaction,
[EntryStates.SKIPPED]: throwWrongSuperType,
[EntryStates.IGNORED]: throwWrongSuperType,
[EntryStates.REMOVED]: throwWrongSuperType,
[EntryStates.SKIPPED]: wrongSuperTypeHandler,
[EntryStates.IGNORED]: wrongSuperTypeHandler,
[EntryStates.REMOVED]: wrongSuperTypeHandler,
},
[EntityTypes.IMAGE]: {
[EntryStates.APPLIED]: () => SuperTypes.Redaction,
[EntryStates.SKIPPED]: () => SuperTypes.Skipped,
[EntryStates.IGNORED]: throwWrongSuperType,
[EntryStates.REMOVED]: throwWrongSuperType,
[EntryStates.IGNORED]: wrongSuperTypeHandler,
[EntryStates.REMOVED]: wrongSuperTypeHandler,
},
[EntityTypes.IMAGE_HINT]: {
[EntryStates.APPLIED]: throwWrongSuperType,
[EntryStates.APPLIED]: wrongSuperTypeHandler,
[EntryStates.SKIPPED]: () => SuperTypes.Hint,
[EntryStates.IGNORED]: throwWrongSuperType,
[EntryStates.REMOVED]: throwWrongSuperType,
[EntryStates.IGNORED]: wrongSuperTypeHandler,
[EntryStates.REMOVED]: wrongSuperTypeHandler,
},
};

View File

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

View File

@ -1,5 +1,4 @@
import { IRectangle } from '../geometry';
import { LogEntryStatus } from './types';
export interface IManualRedactionEntry {
addToDictionary?: boolean;
@ -11,7 +10,6 @@ export interface IManualRedactionEntry {
reason?: string;
requestDate?: string;
softDeletedTime?: string;
status?: LogEntryStatus;
type?: string;
user?: string;
value?: string;

View File

@ -21,10 +21,3 @@ export const ManualRedactionTypes = {
} as const;
export type ManualRedactionType = ValuesOf<typeof ManualRedactionTypes>;
export const LogEntryStatuses = {
APPROVED: 'APPROVED',
DECLINED: 'DECLINED',
} as const;
export type LogEntryStatus = ValuesOf<typeof LogEntryStatuses>;