RED-7619 remove old super type logic, add new

This commit is contained in:
Dan Percic 2023-10-27 21:30:27 +03:00
parent e40a975ac3
commit ffa9a5830f
20 changed files with 180 additions and 344 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.hintDictionary) && !annotation.pending;
canRecategorize && (annotation.isImage || annotation.HINT) && !annotation.pending;
export const canResizeAnnotation = (annotation: AnnotationWrapper, canAddRedaction: boolean) =>
canAddRedaction &&

View File

@ -4,81 +4,67 @@ import {
annotationEntityColorConfig,
AnnotationIconType,
ChangeType,
ChangeTypes,
Dictionary,
Earmark,
EntityTypes,
EntryStates,
EntryTypes,
FalsePositiveSuperTypes,
IComment,
IEntityLogEntry,
ILegalBasis,
IManualChange,
IPoint,
IRectangle,
LogEntryEngine,
LogEntryEngines,
LogEntryStatuses,
LowLevelFilterTypes,
ManualRedactionTypes,
SuperType,
SuperTypeMapper,
SuperTypes,
} from '@red/domain';
import { annotationTypesTranslations } from '@translations/annotation-types-translations';
import { chronologicallyBy, timestampOf } from '../../modules/file-preview/services/file-data.service';
export class AnnotationWrapper implements IListable {
[x: string]: unknown;
id: string;
superType: SuperType;
typeValue: string;
superTypeLabel: string;
type: string;
typeLabel?: string;
color: string;
entity: Dictionary;
comments: IComment[] = [];
firstTopLeftPoint: IPoint;
id: string;
shortContent: string;
content: string;
value: string;
typeLabel: string;
pageNumber: number;
HINT = false;
redaction = false;
status: string;
dictionaryOperation = false;
positions: IRectangle[];
recommendationType: string;
positions: IRectangle[] = [];
legalBasisValue: string;
// AREA === rectangle
AREA = false;
section?: string;
reference: string[];
imported = false;
HINT = false;
IMAGE = false;
section?: string;
reference: string[] = [];
imported = false;
manual = false;
pending = false;
hintDictionary = false;
textAfter?: string;
textBefore?: string;
isChangeLogEntry = false;
changeLogType?: 'ADDED' | 'REMOVED' | 'CHANGED';
engines?: LogEntryEngine[];
engines: LogEntryEngine[] = [];
hasBeenResized: boolean;
hasBeenRecategorized: boolean;
hasLegalBasisChanged: boolean;
hasBeenForcedHint: boolean;
hasBeenForcedRedaction: boolean;
hasBeenRemovedByManualOverride: boolean;
legalBasisList: ILegalBasis[] = [];
isIgnored = false;
get searchKey(): string {
return this.id;
}
get isChangeLogRemoved() {
return this.changeLogType === 'REMOVED';
}
get descriptor() {
return this.isModifyDictionary ? _('dictionary') : _('type');
}
@ -113,10 +99,6 @@ export class AnnotationWrapper implements IListable {
return this.type?.toLowerCase() === 'ocr';
}
get type() {
return this.typeValue;
}
get topLevelFilter() {
return !LowLevelFilterTypes[this.superType];
}
@ -146,7 +128,7 @@ export class AnnotationWrapper implements IListable {
return 'hexagon';
}
if (this.isHint || this.isIgnoredHint) {
if (this.HINT) {
return 'circle';
}
@ -214,33 +196,28 @@ export class AnnotationWrapper implements IListable {
annotationWrapper.id = earmark.id;
annotationWrapper.pageNumber = earmark.positions[0].page;
annotationWrapper.superType = SuperTypes.TextHighlight;
annotationWrapper.typeValue = SuperTypes.TextHighlight;
annotationWrapper.type = SuperTypes.TextHighlight;
annotationWrapper.value = 'Imported';
annotationWrapper.color = earmark.hexColor;
annotationWrapper.positions = earmark.positions;
annotationWrapper.firstTopLeftPoint = earmark.positions[0]?.topLeft;
annotationWrapper.typeLabel = annotationTypesTranslations[annotationWrapper.superType];
annotationWrapper.superTypeLabel = annotationTypesTranslations[annotationWrapper.superType];
return annotationWrapper;
}
static fromData(
logEntry: IEntityLogEntry,
dictionaries: Dictionary[],
dictionary: Dictionary,
changeLogType: ChangeType,
legalBasisList: ILegalBasis[],
hintDictionary: boolean,
isDocumine: boolean,
) {
const annotationWrapper = new AnnotationWrapper();
annotationWrapper.id = logEntry.id;
annotationWrapper.isChangeLogEntry = !!changeLogType;
annotationWrapper.changeLogType = changeLogType;
annotationWrapper.legalBasisList = legalBasisList;
annotationWrapper.redaction = logEntry.entryType === EntryTypes.ENTITY;
annotationWrapper.HINT = logEntry.entryType === EntryTypes.HINT;
annotationWrapper.typeValue = logEntry.type;
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;
@ -253,102 +230,64 @@ export class AnnotationWrapper implements IListable {
annotationWrapper.textBefore = logEntry.textBefore;
annotationWrapper.textAfter = logEntry.textAfter;
annotationWrapper.dictionaryOperation = logEntry.dictionaryEntry;
annotationWrapper.IMAGE = logEntry.entryType === EntryTypes.IMAGE;
annotationWrapper.HINT = logEntry.entryType === EntityTypes.HINT;
annotationWrapper.IMAGE = logEntry.entryType === EntityTypes.IMAGE;
annotationWrapper.AREA = logEntry.entryType === EntityTypes.AREA;
annotationWrapper.isIgnored = logEntry.state === EntryStates.IGNORED;
annotationWrapper.imported = logEntry.imported;
annotationWrapper.legalBasisValue = logEntry.legalBasis;
annotationWrapper.comments = []; //logEntry.comments || [];
annotationWrapper.manual = logEntry.manualChanges?.length > 0;
annotationWrapper.engines = logEntry.engines;
annotationWrapper.engines = logEntry.engines ?? [];
annotationWrapper.section = logEntry.section;
annotationWrapper.reference = logEntry.reference || [];
annotationWrapper.AREA = logEntry.entryType === EntryTypes.AREA;
annotationWrapper.hintDictionary = hintDictionary;
annotationWrapper.hasBeenResized = !!logEntry.manualChanges?.find(
c => c.manualRedactionType === ManualRedactionTypes.RESIZE && c.annotationStatus === LogEntryStatuses.APPROVED,
c => c.manualRedactionType === ManualRedactionTypes.RESIZE && c.processed,
);
annotationWrapper.hasBeenRecategorized = !!logEntry.manualChanges?.find(
c => c.manualRedactionType === ManualRedactionTypes.RECATEGORIZE && c.annotationStatus === LogEntryStatuses.APPROVED,
c => c.manualRedactionType === ManualRedactionTypes.RECATEGORIZE && c.processed,
);
annotationWrapper.hasLegalBasisChanged = !!logEntry.manualChanges?.find(
c => c.manualRedactionType === ManualRedactionTypes.LEGAL_BASIS_CHANGE && c.annotationStatus === LogEntryStatuses.APPROVED,
c => c.manualRedactionType === ManualRedactionTypes.LEGAL_BASIS_CHANGE && c.processed,
);
annotationWrapper.hasBeenForcedHint = !!logEntry.manualChanges?.find(
c => c.manualRedactionType === ManualRedactionTypes.FORCE_HINT && c.annotationStatus === LogEntryStatuses.APPROVED,
c => c.manualRedactionType === ManualRedactionTypes.FORCE_HINT && c.processed,
);
annotationWrapper.hasBeenForcedRedaction = !!logEntry.manualChanges?.find(
c => c.manualRedactionType === ManualRedactionTypes.FORCE_REDACT && c.annotationStatus === LogEntryStatuses.APPROVED,
c => c.manualRedactionType === ManualRedactionTypes.FORCE_REDACT && c.processed,
);
annotationWrapper.hasBeenRemovedByManualOverride = !!logEntry.manualChanges?.find(
c => c.manualRedactionType === ManualRedactionTypes.REMOVE_LOCALLY && c.annotationStatus === LogEntryStatuses.APPROVED,
c => c.manualRedactionType === ManualRedactionTypes.REMOVE_LOCALLY && c.processed,
);
this.#createContent(annotationWrapper, logEntry, isDocumine);
this.#createContent(annotationWrapper, logEntry, isDocumine, legalBasisList);
this.#setSuperType(annotationWrapper, logEntry);
this.#handleRecommendations(annotationWrapper, logEntry);
annotationWrapper.typeLabel = this.#getTypeLabel(annotationWrapper);
annotationWrapper.superTypeLabel = annotationTypesTranslations[annotationWrapper.superType];
const entity = dictionaries.find(d => d.type === annotationWrapper.typeValue);
annotationWrapper.entity = entity?.virtual ? null : entity;
annotationWrapper.typeLabel = dictionary.virtual ? undefined : dictionary.label;
const colorKey = annotationEntityColorConfig[annotationWrapper.superType];
annotationWrapper.color = entity[colorKey] as string;
annotationWrapper.color = dictionary[colorKey] as string;
annotationWrapper['entry'] = logEntry;
return annotationWrapper;
}
static #getTypeLabel(annotation: AnnotationWrapper): string {
if (annotation.superType === SuperTypes.ManualRedaction && annotation.hintDictionary) {
return _('annotation-type.manual-hint');
}
return annotationTypesTranslations[annotation.superType];
}
static #handleRecommendations(annotationWrapper: AnnotationWrapper, logEntry: IEntityLogEntry) {
if (annotationWrapper.superType === SuperTypes.Recommendation) {
annotationWrapper.recommendationType = logEntry.type;
}
}
static #setSuperType(annotationWrapper: AnnotationWrapper, logEntry: IEntityLogEntry) {
if (logEntry.manualChanges?.length) {
const lastRelevantManualChange = logEntry.manualChanges?.at(-1);
const viableChanges = logEntry.changes.filter(c => c.analysisNumber > 1);
const lastChange = viableChanges.sort(chronologicallyBy(x => x.dateTime)).at(-1);
const lastChangeOccurredAfterLastManualChange =
lastChange && timestampOf(lastChange.dateTime) > timestampOf(lastRelevantManualChange.processedDate);
if (
lastChangeOccurredAfterLastManualChange &&
lastChange.type === ChangeTypes.ADDED &&
logEntry.entryType === EntryTypes.ENTITY
) {
annotationWrapper.superType = SuperTypes.Redaction;
return;
}
annotationWrapper.pending = !lastRelevantManualChange.processed;
annotationWrapper.superType = this.#selectSuperType(logEntry, lastRelevantManualChange, annotationWrapper.hintDictionary);
} else {
if (logEntry.state === EntryStates.SKIPPED) {
if (logEntry.entryType === EntryTypes.HINT) {
annotationWrapper.superType = SuperTypes.Hint;
} else {
annotationWrapper.superType = SuperTypes.Skipped;
}
} else if (logEntry.entryType === EntryTypes.RECOMMENDATION) {
annotationWrapper.superType = SuperTypes.Recommendation;
} else if (logEntry.entryType === EntryTypes.ENTITY) {
annotationWrapper.superType = SuperTypes.Redaction;
} else if (logEntry.entryType === EntryTypes.HINT) {
annotationWrapper.superType = SuperTypes.Hint;
} else {
annotationWrapper.superType = SuperTypes.Skipped;
}
}
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) {
static #createContent(
annotationWrapper: AnnotationWrapper,
logEntry: IEntityLogEntry,
isDocumine: boolean,
legalBasisList: ILegalBasis[],
) {
let content = '';
if (logEntry.matchedRule) {
content += `Rule ${logEntry.matchedRule} matched${isDocumine ? ':' : ''} \n\n`;
@ -381,15 +320,13 @@ export class AnnotationWrapper implements IListable {
content += `${prefix} "${logEntry.section}"`;
}
annotationWrapper.shortContent = this.#getShortContent(annotationWrapper) || content;
annotationWrapper.shortContent = this.#getShortContent(annotationWrapper, legalBasisList) || content;
annotationWrapper.content = content;
}
static #getShortContent(annotationWrapper: AnnotationWrapper) {
static #getShortContent(annotationWrapper: AnnotationWrapper, legalBasisList: ILegalBasis[]) {
if (annotationWrapper.legalBasis) {
const lb = annotationWrapper.legalBasisList.find(
lbm => lbm.reason?.toLowerCase().includes(annotationWrapper.legalBasis.toLowerCase()),
);
const lb = legalBasisList.find(lbm => lbm.reason?.toLowerCase().includes(annotationWrapper.legalBasis.toLowerCase()));
if (lb) {
return lb.name;
}
@ -397,98 +334,4 @@ export class AnnotationWrapper implements IListable {
return annotationWrapper.legalBasis;
}
static #selectSuperType(logEntry: IEntityLogEntry, lastManualChange: IManualChange, isHintDictionary: boolean): SuperType {
switch (lastManualChange.manualRedactionType) {
case ManualRedactionTypes.ADD_LOCALLY:
return SuperTypes.ManualRedaction;
case ManualRedactionTypes.ADD_TO_DICTIONARY:
return isHintDictionary ? SuperTypes.Hint : SuperTypes.Redaction;
case ManualRedactionTypes.REMOVE_LOCALLY:
switch (lastManualChange.annotationStatus) {
case LogEntryStatuses.APPROVED:
return isHintDictionary ? SuperTypes.IgnoredHint : SuperTypes.Skipped;
case LogEntryStatuses.DECLINED: {
if (isHintDictionary) {
return SuperTypes.Hint;
}
if (logEntry.entryType === EntryTypes.ENTITY) {
return SuperTypes.Redaction;
}
return SuperTypes.Skipped;
}
}
break;
case ManualRedactionTypes.REMOVE_FROM_DICTIONARY:
if (logEntry.entryType === EntryTypes.ENTITY) {
if (lastManualChange.processed) {
switch (lastManualChange.annotationStatus) {
case LogEntryStatuses.APPROVED:
return SuperTypes.Skipped;
case LogEntryStatuses.DECLINED:
return SuperTypes.Redaction;
}
}
return SuperTypes.Redaction;
}
if (lastManualChange.processed) {
switch (lastManualChange.annotationStatus) {
case LogEntryStatuses.APPROVED:
return logEntry.entryType === EntryTypes.RECOMMENDATION ? SuperTypes.Recommendation : SuperTypes.Skipped;
case LogEntryStatuses.DECLINED:
return isHintDictionary ? SuperTypes.Hint : SuperTypes.Skipped;
}
}
return isHintDictionary ? SuperTypes.Hint : SuperTypes.Skipped;
case ManualRedactionTypes.FORCE_REDACT:
switch (lastManualChange.annotationStatus) {
case LogEntryStatuses.APPROVED:
return SuperTypes.Redaction;
case LogEntryStatuses.DECLINED:
return isHintDictionary ? SuperTypes.IgnoredHint : SuperTypes.Skipped;
}
break;
case ManualRedactionTypes.FORCE_HINT:
switch (lastManualChange.annotationStatus) {
case LogEntryStatuses.APPROVED:
return SuperTypes.Hint;
case LogEntryStatuses.DECLINED:
return SuperTypes.IgnoredHint;
}
break;
case ManualRedactionTypes.RECATEGORIZE:
if (logEntry.entryType === EntryTypes.RECOMMENDATION) {
return SuperTypes.Recommendation;
}
if (logEntry.entryType === EntryTypes.ENTITY) {
return SuperTypes.Redaction;
}
if (logEntry.entryType === EntryTypes.HINT) {
return SuperTypes.Hint;
}
return isHintDictionary ? SuperTypes.IgnoredHint : SuperTypes.Skipped;
case ManualRedactionTypes.LEGAL_BASIS_CHANGE:
return logEntry.type === SuperTypes.ManualRedaction ? SuperTypes.ManualRedaction : SuperTypes.Redaction;
case ManualRedactionTypes.RESIZE:
if (logEntry.entryType === EntryTypes.RECOMMENDATION) {
return SuperTypes.Recommendation;
}
if (logEntry.entryType === EntryTypes.ENTITY) {
return logEntry.type === SuperTypes.ManualRedaction ? SuperTypes.ManualRedaction : SuperTypes.Redaction;
}
if (logEntry.entryType === EntryTypes.HINT) {
return SuperTypes.Hint;
}
return isHintDictionary ? SuperTypes.IgnoredHint : SuperTypes.Skipped;
}
}
}

View File

@ -8,19 +8,19 @@
<div class="flex-1">
<div>
<strong>{{ annotation.typeLabel | translate }}</strong>
<strong>{{ annotation.superTypeLabel | translate }}</strong>
&nbsp;
<strong *ngIf="annotation.pending" class="pending-analysis">
{{ 'annotation.pending' | translate }}
</strong>
</div>
<div *ngIf="annotation.entity">
<div *ngIf="annotation.typeLabel">
<strong>
<span>{{ annotation.descriptor | translate }}</span
>:
</strong>
{{ annotation.entity.label }}
{{ annotation.typeLabel }}
</div>
<div

View File

@ -1,6 +1,6 @@
<div class="active-bar-marker"></div>
<div [class.removed]="annotation.item.isChangeLogRemoved" class="annotation">
<div [class.removed]="annotation.item.isIgnored" class="annotation">
<redaction-annotation-card
[annotation]="annotation.item"
[isSelected]="annotation.isSelected"
@ -11,7 +11,7 @@
<div *ngIf="!annotation.item.isEarmark" class="actions-wrapper">
<div
(click)="comments.toggleExpandComments()"
[matTooltip]="'comments.comments' | translate : { count: annotation.item.comments?.length }"
[matTooltip]="'comments.comments' | translate: { count: annotation.item.comments?.length }"
class="comments-counter"
iqserStopPropagation
matTooltipPosition="above"

View File

@ -28,12 +28,12 @@ export class AnnotationWrapperComponent implements OnChanges {
#getActionsHelpModeKey(): string {
if (!this.#isDocumine) {
const type = this.annotation.item.typeLabel?.split('.')[1];
const typeValue = this.annotation.item.typeValue;
if (type === 'hint' && (typeValue === 'ocr' || typeValue === 'formula' || typeValue === 'image')) {
return ActionsHelpModeKeys[`${type}-${typeValue}`];
const superType = this.annotation.item.superTypeLabel?.split('.')[1];
const type = this.annotation.item.type;
if (superType === 'hint' && (type === 'ocr' || type === 'formula' || type === 'image')) {
return ActionsHelpModeKeys[`${superType}-${type}`];
}
return ActionsHelpModeKeys[type];
return ActionsHelpModeKeys[superType];
}
return '';
}

View File

@ -356,7 +356,7 @@ export class FileWorkloadComponent extends AutoUnsubscribe implements OnInit, On
}
if (this.viewModeService.isRedacted()) {
annotations = annotations.filter(a => !bool(a.isChangeLogRemoved));
annotations = annotations.filter(a => !bool(a.isIgnored));
}
if (this.#isDocumine && !this.#isIqserDevMode) {

View File

@ -5,7 +5,7 @@
<div class="dialog-content redaction">
<div class="iqser-input-group w-450">
<label [translate]="'resize-annotation.dialog.content.original-text'" class="selected-text"></label>
{{ redaction.value }}
{{ data.redaction.value }}
</div>
<div class="iqser-input-group w-450">

View File

@ -1,8 +1,6 @@
import { Component, OnInit } from '@angular/core';
import { Component, inject } from '@angular/core';
import { FormBuilder } from '@angular/forms';
import { IconButtonTypes, IqserDialogComponent } from '@iqser/common-ui';
import { AnnotationWrapper } from '@models/file/annotation.wrapper';
import { Dictionary, Dossier } from '@red/domain';
import { ActiveDossiersService } from '@services/dossiers/active-dossiers.service';
import { DictionariesMapService } from '@services/entity-services/dictionaries-map.service';
import { ResizeAnnotationData, ResizeAnnotationResult } from '../../../utils/dialog-types';
@ -10,51 +8,26 @@ import { ResizeAnnotationData, ResizeAnnotationResult } from '../../../utils/dia
@Component({
templateUrl: './resize-annotation-dialog.component.html',
})
export class ResizeAnnotationDialogComponent
extends IqserDialogComponent<ResizeAnnotationDialogComponent, ResizeAnnotationData, ResizeAnnotationResult>
implements OnInit
{
readonly #dossier: Dossier;
export class ResizeAnnotationDialogComponent extends IqserDialogComponent<
ResizeAnnotationDialogComponent,
ResizeAnnotationData,
ResizeAnnotationResult
> {
readonly #dossier = inject(ActiveDossiersService).find(this.data.dossierId);
readonly iconButtonTypes = IconButtonTypes;
dictionaries: Dictionary[] = [];
redaction: AnnotationWrapper;
readonly form = this.#getForm();
constructor(
activeDossiersService: ActiveDossiersService,
private readonly _dictionariesMapService: DictionariesMapService,
private readonly _formBuilder: FormBuilder,
) {
super();
this.#dossier = activeDossiersService.find(this.data.dossierId);
this.redaction = this.data.redaction;
}
get displayedDictionaryLabel() {
const dictType = this.form.get('dictionary').value;
if (dictType) {
return this.dictionaries.find(d => d.type === dictType)?.label ?? null;
}
return null;
}
ngOnInit() {
this.dictionaries = this._dictionariesMapService.get(this.#dossier.dossierTemplateId);
}
readonly dictionaries = inject(DictionariesMapService).get(this.#dossier.dossierTemplateId);
readonly entity = this.dictionaries.find(d => d.type === this.data.redaction.type);
readonly form = inject(FormBuilder).group({
comment: [null],
});
save() {
const formValue = this.form.getRawValue();
this.close({
comment: formValue.comment,
updateDictionary: this.redaction.entity.hasDictionary,
addToAllDossiers: this.redaction.entity.hasDictionary,
});
}
#getForm() {
return this._formBuilder.group({
comment: [null],
updateDictionary: this.entity.hasDictionary,
addToAllDossiers: this.entity.hasDictionary,
});
}
}

View File

@ -26,8 +26,8 @@
<mat-form-field>
<mat-select formControlName="dictionary">
<mat-select-trigger>{{ displayedDictionaryLabel }}</mat-select-trigger>
<mat-option [value]="redaction.entity.type">
<span> {{ redaction.entity.label }} </span>
<mat-option [value]="entity.type">
<span> {{ redaction.typeLabel }} </span>
</mat-option>
</mat-select>
</mat-form-field>

View File

@ -1,7 +1,6 @@
import { Component, inject, OnInit } from '@angular/core';
import { Component, inject } from '@angular/core';
import { FormBuilder, FormControl, FormGroup } from '@angular/forms';
import { DetailsRadioOption, IconButtonTypes, IqserDialogComponent } from '@iqser/common-ui';
import { Dictionary } from '@red/domain';
import { ActiveDossiersService } from '@services/dossiers/active-dossiers.service';
import { DictionariesMapService } from '@services/entity-services/dictionaries-map.service';
import { getResizeRedactionOptions, ResizeOptions, ResizeRedactionOption } from '../../utils/dialog-options';
@ -10,15 +9,17 @@ import { ResizeRedactionData, ResizeRedactionResult } from '../../utils/dialog-t
@Component({
templateUrl: './resize-redaction-dialog.component.html',
})
export class ResizeRedactionDialogComponent
extends IqserDialogComponent<ResizeRedactionDialogComponent, ResizeRedactionData, ResizeRedactionResult>
implements OnInit
{
export class ResizeRedactionDialogComponent extends IqserDialogComponent<
ResizeRedactionDialogComponent,
ResizeRedactionData,
ResizeRedactionResult
> {
readonly #applyToAllDossiers = this.data.applyToAllDossiers ?? true;
readonly #dossier = inject(ActiveDossiersService).find(this.data.dossierId);
readonly iconButtonTypes = IconButtonTypes;
readonly options: DetailsRadioOption<ResizeRedactionOption>[];
dictionaries: Dictionary[] = [];
readonly dictionaries = inject(DictionariesMapService).get(this.#dossier.dossierTemplateId);
readonly entity = this.dictionaries.find(d => d.type === this.data.redaction.type);
readonly redaction = this.data.redaction;
readonly form: FormGroup<{
comment: FormControl<string>;
@ -26,10 +27,7 @@ export class ResizeRedactionDialogComponent
option: FormControl<DetailsRadioOption<ResizeRedactionOption>>;
}>;
constructor(
private readonly _dictionariesMapService: DictionariesMapService,
private readonly _formBuilder: FormBuilder,
) {
constructor(private readonly _formBuilder: FormBuilder) {
super();
this.options = getResizeRedactionOptions(this.redaction, this.#dossier, false, this.#applyToAllDossiers, this.data.isApprover);
this.form = this.#getForm();
@ -43,10 +41,6 @@ export class ResizeRedactionDialogComponent
return null;
}
ngOnInit() {
this.dictionaries = this._dictionariesMapService.get(this.#dossier.dossierTemplateId);
}
save() {
const formValue = this.form.getRawValue();
const updateDictionary = formValue.option.value === ResizeOptions.IN_DOSSIER;
@ -61,7 +55,7 @@ export class ResizeRedactionDialogComponent
#getForm() {
return this._formBuilder.group({
comment: [null],
dictionary: new FormControl({ value: this.redaction.entity.type, disabled: true }),
dictionary: new FormControl({ value: this.redaction.typeLabel, disabled: true }),
option: this.options[0],
});
}

View File

@ -106,7 +106,7 @@ export class AnnotationProcessingService {
}
const childFilter: IFilter = {
id: a.filterKey,
label: a.entity.label,
label: a.typeLabel,
checked: false,
matches: 1,
metadata: {

View File

@ -3,7 +3,7 @@ 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, File, IEntityLog, IEntityLogEntry, LogEntryStatuses, ViewedPage, ViewMode, ViewModes } from '@red/domain';
import { ChangeType, ChangeTypes, EntryStates, File, IEntityLog, IEntityLogEntry, 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';
@ -154,7 +154,7 @@ export class FileDataService extends EntitiesService<AnnotationWrapper, Annotati
#getVisibleAnnotations(annotations: AnnotationWrapper[], viewMode: ViewMode) {
return annotations.filter(annotation => {
if (viewMode === 'STANDARD') {
return !annotation.isChangeLogRemoved;
return !annotation.isIgnored;
}
if (viewMode === 'DELTA') {
@ -177,13 +177,12 @@ export class FileDataService extends EntitiesService<AnnotationWrapper, Annotati
for (const entry of entityLog.entityLogEntry) {
const pageNumber = entry.positions[0].pageNumber;
const manual = entry.manualChanges?.length > 0;
const manual = entry.manualChanges.length > 0;
if (!manual && file.excludedPages.includes(pageNumber)) {
continue;
}
const changeLogValues = this.#getChangeLogValues(entry, file);
if (changeLogValues.hidden) {
if (entry.state === EntryStates.REMOVED) {
continue;
}
@ -200,14 +199,8 @@ export class FileDataService extends EntitiesService<AnnotationWrapper, Annotati
continue;
}
const annotation = AnnotationWrapper.fromData(
entry,
dictionaries,
changeLogValues.changeLogType,
entityLog.legalBasis ?? [],
!!dictionary?.hint,
this.#isDocumine,
);
const changeType = this.#getChangeLogType(entry, file);
const annotation = AnnotationWrapper.fromData(entry, dictionary, changeType, entityLog.legalBasis ?? [], this.#isDocumine);
annotations.push(annotation);
}
@ -215,32 +208,20 @@ export class FileDataService extends EntitiesService<AnnotationWrapper, Annotati
return annotations;
}
#getChangeLogValues(
entityLogEntry: IEntityLogEntry,
file: File,
): {
hidden: boolean;
changeLogType?: ChangeType;
} {
#getChangeLogType(entityLogEntry: IEntityLogEntry, file: File): ChangeType | undefined {
const hasManualChanges = entityLogEntry.manualChanges?.length > 0;
if (file.numberOfAnalyses <= 1 && !file.hasUpdates && !hasManualChanges) {
return {
changeLogType: undefined,
hidden: false,
};
return;
}
const viableChanges = entityLogEntry.changes?.filter(c => c.analysisNumber > 1);
const lastChange = viableChanges?.sort(chronologicallyBy(x => x.dateTime)).at(-1);
const page = entityLogEntry.positions?.[0].pageNumber;
const page = entityLogEntry.positions[0].pageNumber;
const viewedPage = this.#originalViewedPages.find(p => p.page === page);
if (!viewedPage) {
return {
changeLogType: undefined,
hidden: lastChange ? lastChange.type === ChangeTypes.REMOVED : false,
};
return;
}
// page has been seen -> let's see if it's a change
@ -249,14 +230,10 @@ export class FileDataService extends EntitiesService<AnnotationWrapper, Annotati
if (changeOccurredAfterPageIsViewed) {
this.#markPageAsUnseenIfNeeded(viewedPage, lastChange.dateTime);
return {
changeLogType: lastChange?.type,
hidden: false,
};
return lastChange?.type;
}
const visibleStatuses: string[] = [LogEntryStatuses.APPROVED];
const viableManualChanges = entityLogEntry.manualChanges.filter(change => visibleStatuses.includes(change.annotationStatus));
const viableManualChanges = entityLogEntry.manualChanges.filter(change => change.processed);
viableManualChanges.sort(chronologicallyBy(x => x.processedDate));
const lastManualChange = viableManualChanges.at(-1);
const processedTime = lastManualChange?.processedDate;
@ -264,17 +241,8 @@ export class FileDataService extends EntitiesService<AnnotationWrapper, Annotati
if (changeOccurredAfterPageIsViewed) {
this.#markPageAsUnseenIfNeeded(viewedPage, processedTime);
return {
changeLogType: ChangeTypes.CHANGED,
hidden: false,
};
return ChangeTypes.CHANGED;
}
// Page doesn't have a view-time or no relevant changes - hide removed anyway
return {
changeLogType: undefined,
hidden: lastChange ? lastChange.type === ChangeTypes.REMOVED : false,
};
}
#markPageAsUnseenIfNeeded(viewedPage: ViewedPage, processedTime: string) {

View File

@ -147,7 +147,7 @@ export class AnnotationDrawService {
const annotation = this._pdf.textHighlight();
annotation.Quads = this.#rectanglesToQuads(annotationWrapper.positions, pageNumber);
annotation.Opacity = annotationWrapper.isChangeLogRemoved ? DEFAULT_REMOVED_ANNOTATION_OPACITY : DEFAULT_TEXT_ANNOTATION_OPACITY;
annotation.Opacity = annotationWrapper.isIgnored ? DEFAULT_REMOVED_ANNOTATION_OPACITY : DEFAULT_TEXT_ANNOTATION_OPACITY;
annotation.setContents(annotationWrapper.content);
annotation.PageNumber = pageNumber;
annotation.StrokeColor = this.convertColor(annotationWrapper.color);
@ -159,14 +159,14 @@ export class AnnotationDrawService {
this._annotationManager.addToHidden(annotationWrapper.id);
}
annotation.Hidden =
annotationWrapper.isChangeLogRemoved ||
annotationWrapper.isIgnored ||
(hideSkipped && annotationWrapper.isSkipped) ||
this._annotationManager.isHidden(annotationWrapper.id);
annotation.setCustomData('redact-manager', 'true');
annotation.setCustomData('redaction', String(annotationWrapper.previewAnnotation));
annotation.setCustomData('skipped', String(annotationWrapper.isSkipped));
annotation.setCustomData('changeLog', String(annotationWrapper.isChangeLogEntry));
annotation.setCustomData('changeLogRemoved', String(annotationWrapper.isChangeLogRemoved));
annotation.setCustomData('changeLogRemoved', String(annotationWrapper.isIgnored));
annotation.setCustomData('opacity', String(annotation.Opacity));
const redactionColor = this._defaultColorsService.getColor(dossierTemplateId, 'previewColor');

View File

@ -8,6 +8,7 @@ export const annotationTypesTranslations: Record<TranslationKey, string> = {
[SuperTypes.Hint]: _('annotation-type.hint'),
[SuperTypes.IgnoredHint]: _('annotation-type.ignored-hint'),
[SuperTypes.ManualRedaction]: _('annotation-type.manual-redaction'),
[SuperTypes.ManualHint]: _('annotation-type.manual-hint'),
[SuperTypes.Recommendation]: _('annotation-type.recommendation'),
[SuperTypes.Redaction]: _('annotation-type.redaction'),
[SuperTypes.Skipped]: _('annotation-type.skipped'),

View File

@ -7,5 +7,6 @@ export const SuperTypeSorter: { [key in SuperType]: number } = {
[SuperTypes.Skipped]: 50,
[SuperTypes.Redaction]: 1,
[SuperTypes.ManualRedaction]: 2,
[SuperTypes.ManualHint]: 3,
[SuperTypes.Hint]: 19,
};

View File

@ -1,15 +1,14 @@
import { SuperType, SuperTypes } from '../files';
import { DefaultColorType } from './default-color-type';
export type DefaultBasedColorType = SuperType | 'updated' | 'analysis';
export type DefaultBasedColorType = Exclude<SuperType, typeof SuperTypes.TextHighlight> | 'updated' | 'analysis';
export const annotationDefaultColorConfig: Record<DefaultBasedColorType, DefaultColorType> = {
[SuperTypes.TextHighlight]: 'redactionColor', // not actually used
[SuperTypes.IgnoredHint]: 'ignoredHintColor',
[SuperTypes.Skipped]: 'skippedColor',
[SuperTypes.Redaction]: 'redactionColor',
[SuperTypes.ManualRedaction]: 'redactionColor',
[SuperTypes.ManualHint]: 'redactionColor',
[SuperTypes.Recommendation]: 'recommendationColor',
[SuperTypes.Hint]: 'hintColor',
@ -17,19 +16,15 @@ export const annotationDefaultColorConfig: Record<DefaultBasedColorType, Default
analysis: 'analysisColor',
} as const;
type EntityBasedColorType =
| typeof SuperTypes.IgnoredHint
| typeof SuperTypes.Skipped
| typeof SuperTypes.Redaction
| typeof SuperTypes.ManualRedaction
| typeof SuperTypes.Recommendation
| typeof SuperTypes.Hint;
export const annotationEntityColorConfig: Record<EntityBasedColorType, 'hexColor' | 'recommendationHexColor' | 'skippedHexColor'> = {
export const annotationEntityColorConfig: Record<
Exclude<SuperType, typeof SuperTypes.TextHighlight>,
'hexColor' | 'recommendationHexColor' | 'skippedHexColor'
> = {
[SuperTypes.IgnoredHint]: 'skippedHexColor',
[SuperTypes.Skipped]: 'skippedHexColor',
[SuperTypes.Redaction]: 'hexColor',
[SuperTypes.ManualRedaction]: 'hexColor',
[SuperTypes.ManualHint]: 'hexColor',
[SuperTypes.Recommendation]: 'recommendationHexColor',
[SuperTypes.Hint]: 'hexColor',
} as const;

View File

@ -1,4 +1,5 @@
import { ValuesOf } from '@iqser/common-ui/lib/utils';
import { EntityType, EntityTypes, EntryState, EntryStates, IEntityLogEntry } from '../redaction-log';
export const SuperTypes = {
TextHighlight: 'text-highlight',
@ -6,12 +7,72 @@ export const SuperTypes = {
Skipped: 'skipped',
Redaction: 'redaction',
ManualRedaction: 'manual',
ManualHint: 'manual-hint',
Recommendation: 'recommendation',
Hint: 'hint',
} as const;
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`);
}
/**
* https://knecon.atlassian.net/wiki/spaces/RED/pages/102072322/EntityLog+-+Enum+combinations
*/
export const SuperTypeMapper: Record<EntityType, Record<EntryState, (entry: IEntityLogEntry) => SuperType>> = {
[EntityTypes.ENTITY]: {
[EntryStates.APPLIED]: entry => (entry.manualChanges.length ? SuperTypes.ManualRedaction : SuperTypes.Redaction),
[EntryStates.SKIPPED]: () => SuperTypes.Skipped,
[EntryStates.IGNORED]: () => SuperTypes.Redaction,
[EntryStates.REMOVED]: throwWrongSuperType,
},
[EntityTypes.HINT]: {
[EntryStates.APPLIED]: entry => (entry.manualChanges.length ? SuperTypes.ManualHint : SuperTypes.Hint),
[EntryStates.SKIPPED]: entry => (entry.manualChanges.length ? SuperTypes.ManualHint : SuperTypes.Hint),
[EntryStates.IGNORED]: () => SuperTypes.IgnoredHint,
[EntryStates.REMOVED]: throwWrongSuperType,
},
[EntityTypes.FALSE_POSITIVE]: {
[EntryStates.APPLIED]: throwWrongSuperType,
[EntryStates.SKIPPED]: throwWrongSuperType,
[EntryStates.IGNORED]: throwWrongSuperType,
[EntryStates.REMOVED]: throwWrongSuperType,
},
[EntityTypes.RECOMMENDATION]: {
[EntryStates.APPLIED]: throwWrongSuperType,
[EntryStates.SKIPPED]: () => SuperTypes.Recommendation,
[EntryStates.IGNORED]: throwWrongSuperType,
[EntryStates.REMOVED]: throwWrongSuperType,
},
[EntityTypes.FALSE_RECOMMENDATION]: {
[EntryStates.APPLIED]: throwWrongSuperType,
[EntryStates.SKIPPED]: throwWrongSuperType,
[EntryStates.IGNORED]: throwWrongSuperType,
[EntryStates.REMOVED]: throwWrongSuperType,
},
[EntityTypes.AREA]: {
[EntryStates.APPLIED]: () => SuperTypes.Redaction,
[EntryStates.SKIPPED]: throwWrongSuperType,
[EntryStates.IGNORED]: throwWrongSuperType,
[EntryStates.REMOVED]: throwWrongSuperType,
},
[EntityTypes.IMAGE]: {
[EntryStates.APPLIED]: () => SuperTypes.Redaction,
[EntryStates.SKIPPED]: () => SuperTypes.Skipped,
[EntryStates.IGNORED]: throwWrongSuperType,
[EntryStates.REMOVED]: throwWrongSuperType,
},
[EntityTypes.IMAGE_HINT]: {
[EntryStates.APPLIED]: throwWrongSuperType,
[EntryStates.SKIPPED]: () => SuperTypes.Hint,
[EntryStates.IGNORED]: throwWrongSuperType,
[EntryStates.REMOVED]: throwWrongSuperType,
},
};
export const LowLevelFilterTypes = {
[SuperTypes.Hint]: true,
[SuperTypes.Redaction]: true,

View File

@ -1,7 +1,7 @@
import { ITrackable } from '@iqser/common-ui';
import { IChange } from './change';
import { EntryState } from './entity-states';
import { EntryType } from './entry-types';
import { EntityType } from './entity-types';
import { IManualChange } from './manual-change';
import { LogEntryEngine } from './types';
@ -12,7 +12,7 @@ export interface IEntityLogEntryPosition {
export interface IEntityLogEntry extends ITrackable {
type: string;
entryType: EntryType;
entryType: EntityType;
state: EntryState;
value: string;
reason: string;

View File

@ -1,4 +1,4 @@
export const EntryTypes = {
export const EntityTypes = {
ENTITY: 'ENTITY',
HINT: 'HINT',
FALSE_POSITIVE: 'FALSE_POSITIVE',
@ -9,4 +9,4 @@ export const EntryTypes = {
IMAGE_HINT: 'IMAGE_HINT',
} as const;
export type EntryType = (typeof EntryTypes)[keyof typeof EntryTypes];
export type EntityType = (typeof EntityTypes)[keyof typeof EntityTypes];

View File

@ -15,4 +15,4 @@ export * from './dictionary-entry-types';
export * from './entity-log';
export * from './entity-log-entry';
export * from './entity-states';
export * from './entry-types';
export * from './entity-types';