RED-7619 wip migration to entity log
This commit is contained in:
parent
e3463907f6
commit
c94ef48844
@ -156,7 +156,7 @@ export const appModuleFactory = (config: AppConfig) => {
|
|||||||
enabled: false,
|
enabled: false,
|
||||||
},
|
},
|
||||||
REDACTION_LOG: {
|
REDACTION_LOG: {
|
||||||
enabled: false,
|
enabled: true,
|
||||||
},
|
},
|
||||||
VIEWED_PAGES: {
|
VIEWED_PAGES: {
|
||||||
enabled: false,
|
enabled: false,
|
||||||
|
|||||||
@ -9,13 +9,15 @@ import {
|
|||||||
DefaultColors,
|
DefaultColors,
|
||||||
Dictionary,
|
Dictionary,
|
||||||
Earmark,
|
Earmark,
|
||||||
|
EntryStates,
|
||||||
|
EntryTypes,
|
||||||
FalsePositiveSuperTypes,
|
FalsePositiveSuperTypes,
|
||||||
IComment,
|
IComment,
|
||||||
|
IEntityLogEntry,
|
||||||
ILegalBasis,
|
ILegalBasis,
|
||||||
IManualChange,
|
IManualChange,
|
||||||
IPoint,
|
IPoint,
|
||||||
IRectangle,
|
IRectangle,
|
||||||
IRedactionLogEntry,
|
|
||||||
LogEntryEngine,
|
LogEntryEngine,
|
||||||
LogEntryEngines,
|
LogEntryEngines,
|
||||||
LogEntryStatuses,
|
LogEntryStatuses,
|
||||||
@ -296,7 +298,7 @@ export class AnnotationWrapper implements IListable {
|
|||||||
}
|
}
|
||||||
|
|
||||||
static fromData(
|
static fromData(
|
||||||
redactionLogEntry: IRedactionLogEntry,
|
logEntry: IEntityLogEntry,
|
||||||
dictionaries: Dictionary[],
|
dictionaries: Dictionary[],
|
||||||
defaultColors: DefaultColors,
|
defaultColors: DefaultColors,
|
||||||
changeLogType: ChangeType,
|
changeLogType: ChangeType,
|
||||||
@ -306,56 +308,61 @@ export class AnnotationWrapper implements IListable {
|
|||||||
) {
|
) {
|
||||||
const annotationWrapper = new AnnotationWrapper();
|
const annotationWrapper = new AnnotationWrapper();
|
||||||
|
|
||||||
annotationWrapper.id = redactionLogEntry.id;
|
annotationWrapper.id = logEntry.id;
|
||||||
annotationWrapper.isChangeLogEntry = !!changeLogType;
|
annotationWrapper.isChangeLogEntry = !!changeLogType;
|
||||||
annotationWrapper.changeLogType = changeLogType;
|
annotationWrapper.changeLogType = changeLogType;
|
||||||
annotationWrapper.legalBasisList = legalBasisList;
|
annotationWrapper.legalBasisList = legalBasisList;
|
||||||
annotationWrapper.redaction = redactionLogEntry.redacted;
|
annotationWrapper.redaction = logEntry.entryType === EntryTypes.ENTITY;
|
||||||
annotationWrapper.hint = redactionLogEntry.hint;
|
annotationWrapper.hint = logEntry.entryType === EntryTypes.HINT;
|
||||||
annotationWrapper.typeValue = redactionLogEntry.type;
|
annotationWrapper.typeValue = logEntry.type;
|
||||||
annotationWrapper.value = redactionLogEntry.value;
|
annotationWrapper.value = logEntry.value;
|
||||||
annotationWrapper.firstTopLeftPoint = redactionLogEntry.positions[0]?.topLeft;
|
annotationWrapper.firstTopLeftPoint = { x: logEntry.positions[0].rectangle[0], y: logEntry.positions[0].rectangle[1] };
|
||||||
annotationWrapper.pageNumber = redactionLogEntry.positions[0]?.page;
|
annotationWrapper.pageNumber = logEntry.positions[0].pageNumber;
|
||||||
annotationWrapper.positions = redactionLogEntry.positions;
|
annotationWrapper.positions = logEntry.positions.map(p => ({
|
||||||
annotationWrapper.textBefore = redactionLogEntry.textBefore;
|
page: p.pageNumber,
|
||||||
annotationWrapper.textAfter = redactionLogEntry.textAfter;
|
height: p.rectangle[3],
|
||||||
annotationWrapper.dictionaryOperation = redactionLogEntry.dictionaryEntry;
|
width: p.rectangle[2],
|
||||||
annotationWrapper.image = redactionLogEntry.image;
|
topLeft: { x: p.rectangle[0], y: p.rectangle[1] },
|
||||||
annotationWrapper.imported = redactionLogEntry.imported;
|
}));
|
||||||
annotationWrapper.legalBasisValue = redactionLogEntry.legalBasis;
|
annotationWrapper.textBefore = logEntry.textBefore;
|
||||||
annotationWrapper.comments = redactionLogEntry.comments || [];
|
annotationWrapper.textAfter = logEntry.textAfter;
|
||||||
annotationWrapper.manual = redactionLogEntry.manualChanges?.length > 0;
|
annotationWrapper.dictionaryOperation = logEntry.dictionaryEntry;
|
||||||
annotationWrapper.engines = redactionLogEntry.engines;
|
annotationWrapper.image = logEntry.entryType === EntryTypes.IMAGE;
|
||||||
annotationWrapper.section = redactionLogEntry.section;
|
annotationWrapper.imported = logEntry.imported;
|
||||||
annotationWrapper.reference = redactionLogEntry.reference || [];
|
annotationWrapper.legalBasisValue = logEntry.legalBasis;
|
||||||
annotationWrapper.rectangle = redactionLogEntry.rectangle;
|
annotationWrapper.comments = []; //logEntry.comments || [];
|
||||||
|
annotationWrapper.manual = logEntry.manualChanges?.length > 0;
|
||||||
|
annotationWrapper.engines = logEntry.engines;
|
||||||
|
annotationWrapper.section = logEntry.section;
|
||||||
|
annotationWrapper.reference = logEntry.reference || [];
|
||||||
|
annotationWrapper.rectangle = logEntry.entryType === EntryTypes.AREA;
|
||||||
annotationWrapper.hintDictionary = hintDictionary;
|
annotationWrapper.hintDictionary = hintDictionary;
|
||||||
annotationWrapper.hasBeenResized = !!redactionLogEntry.manualChanges?.find(
|
annotationWrapper.hasBeenResized = !!logEntry.manualChanges?.find(
|
||||||
c => c.manualRedactionType === ManualRedactionTypes.RESIZE && c.annotationStatus === LogEntryStatuses.APPROVED,
|
c => c.manualRedactionType === ManualRedactionTypes.RESIZE && c.annotationStatus === LogEntryStatuses.APPROVED,
|
||||||
);
|
);
|
||||||
annotationWrapper.hasBeenRecategorized = !!redactionLogEntry.manualChanges?.find(
|
annotationWrapper.hasBeenRecategorized = !!logEntry.manualChanges?.find(
|
||||||
c => c.manualRedactionType === ManualRedactionTypes.RECATEGORIZE && c.annotationStatus === LogEntryStatuses.APPROVED,
|
c => c.manualRedactionType === ManualRedactionTypes.RECATEGORIZE && c.annotationStatus === LogEntryStatuses.APPROVED,
|
||||||
);
|
);
|
||||||
annotationWrapper.hasLegalBasisChanged = !!redactionLogEntry.manualChanges?.find(
|
annotationWrapper.hasLegalBasisChanged = !!logEntry.manualChanges?.find(
|
||||||
c => c.manualRedactionType === ManualRedactionTypes.LEGAL_BASIS_CHANGE && c.annotationStatus === LogEntryStatuses.APPROVED,
|
c => c.manualRedactionType === ManualRedactionTypes.LEGAL_BASIS_CHANGE && c.annotationStatus === LogEntryStatuses.APPROVED,
|
||||||
);
|
);
|
||||||
annotationWrapper.hasBeenForcedHint = !!redactionLogEntry.manualChanges?.find(
|
annotationWrapper.hasBeenForcedHint = !!logEntry.manualChanges?.find(
|
||||||
c => c.manualRedactionType === ManualRedactionTypes.FORCE_HINT && c.annotationStatus === LogEntryStatuses.APPROVED,
|
c => c.manualRedactionType === ManualRedactionTypes.FORCE_HINT && c.annotationStatus === LogEntryStatuses.APPROVED,
|
||||||
);
|
);
|
||||||
annotationWrapper.hasBeenForcedRedaction = !!redactionLogEntry.manualChanges?.find(
|
annotationWrapper.hasBeenForcedRedaction = !!logEntry.manualChanges?.find(
|
||||||
c => c.manualRedactionType === ManualRedactionTypes.FORCE_REDACT && c.annotationStatus === LogEntryStatuses.APPROVED,
|
c => c.manualRedactionType === ManualRedactionTypes.FORCE_REDACT && c.annotationStatus === LogEntryStatuses.APPROVED,
|
||||||
);
|
);
|
||||||
annotationWrapper.hasBeenRemovedByManualOverride = !!redactionLogEntry.manualChanges?.find(
|
annotationWrapper.hasBeenRemovedByManualOverride = !!logEntry.manualChanges?.find(
|
||||||
c => c.manualRedactionType === ManualRedactionTypes.REMOVE_LOCALLY && c.annotationStatus === LogEntryStatuses.APPROVED,
|
c => c.manualRedactionType === ManualRedactionTypes.REMOVE_LOCALLY && c.annotationStatus === LogEntryStatuses.APPROVED,
|
||||||
);
|
);
|
||||||
annotationWrapper.legalBasisChangeValue = redactionLogEntry.manualChanges?.find(
|
annotationWrapper.legalBasisChangeValue = logEntry.manualChanges?.find(
|
||||||
c => c.manualRedactionType === ManualRedactionTypes.LEGAL_BASIS_CHANGE && c.annotationStatus === LogEntryStatuses.REQUESTED,
|
c => c.manualRedactionType === ManualRedactionTypes.LEGAL_BASIS_CHANGE && c.annotationStatus === LogEntryStatuses.REQUESTED,
|
||||||
)?.propertyChanges.legalBasis;
|
)?.propertyChanges.legalBasis;
|
||||||
|
|
||||||
this.#createContent(annotationWrapper, redactionLogEntry, isDocumine);
|
this.#createContent(annotationWrapper, logEntry, isDocumine);
|
||||||
this.#setSuperType(annotationWrapper, redactionLogEntry);
|
this.#setSuperType(annotationWrapper, logEntry);
|
||||||
this.#handleRecommendations(annotationWrapper, redactionLogEntry);
|
this.#handleRecommendations(annotationWrapper, logEntry);
|
||||||
annotationWrapper.typeLabel = this.#getTypeLabel(redactionLogEntry, annotationWrapper);
|
annotationWrapper.typeLabel = this.#getTypeLabel(logEntry, annotationWrapper);
|
||||||
|
|
||||||
const entity = dictionaries.find(d => d.type === annotationWrapper.typeValue);
|
const entity = dictionaries.find(d => d.type === annotationWrapper.typeValue);
|
||||||
annotationWrapper.entity = entity?.virtual ? null : entity;
|
annotationWrapper.entity = entity?.virtual ? null : entity;
|
||||||
@ -373,8 +380,8 @@ export class AnnotationWrapper implements IListable {
|
|||||||
return annotationWrapper;
|
return annotationWrapper;
|
||||||
}
|
}
|
||||||
|
|
||||||
static #getTypeLabel(redactionLogEntry: IRedactionLogEntry, annotation: AnnotationWrapper): string {
|
static #getTypeLabel(logEntry: IEntityLogEntry, annotation: AnnotationWrapper): string {
|
||||||
if (redactionLogEntry.reason?.toLowerCase() === 'false positive') {
|
if (logEntry.reason?.toLowerCase() === 'false positive') {
|
||||||
return annotationTypesTranslations[SuggestionAddFalsePositive];
|
return annotationTypesTranslations[SuggestionAddFalsePositive];
|
||||||
}
|
}
|
||||||
if (annotation.superType === SuperTypes.ManualRedaction && annotation.hintDictionary) {
|
if (annotation.superType === SuperTypes.ManualRedaction && annotation.hintDictionary) {
|
||||||
@ -383,38 +390,48 @@ export class AnnotationWrapper implements IListable {
|
|||||||
return annotationTypesTranslations[annotation.superType];
|
return annotationTypesTranslations[annotation.superType];
|
||||||
}
|
}
|
||||||
|
|
||||||
static #handleRecommendations(annotationWrapper: AnnotationWrapper, redactionLogEntry: IRedactionLogEntry) {
|
static #handleRecommendations(annotationWrapper: AnnotationWrapper, logEntry: IEntityLogEntry) {
|
||||||
if (annotationWrapper.superType === SuperTypes.Recommendation) {
|
if (annotationWrapper.superType === SuperTypes.Recommendation) {
|
||||||
annotationWrapper.recommendationType = redactionLogEntry.type;
|
annotationWrapper.recommendationType = logEntry.type;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
static #setSuperType(annotationWrapper: AnnotationWrapper, entry: IRedactionLogEntry) {
|
static #setSuperType(annotationWrapper: AnnotationWrapper, logEntry: IEntityLogEntry) {
|
||||||
if (entry.manualChanges?.length) {
|
if (logEntry.manualChanges?.length) {
|
||||||
const lastRelevantManualChange = entry.manualChanges?.at(-1);
|
const lastRelevantManualChange = logEntry.manualChanges?.at(-1);
|
||||||
const viableChanges = entry.changes.filter(c => c.analysisNumber > 1);
|
const viableChanges = logEntry.changes.filter(c => c.analysisNumber > 1);
|
||||||
const lastChange = viableChanges.sort(chronologicallyBy(x => x.dateTime)).at(-1);
|
const lastChange = viableChanges.sort(chronologicallyBy(x => x.dateTime)).at(-1);
|
||||||
const lastChangeOccurredAfterLastManualChange =
|
const lastChangeOccurredAfterLastManualChange =
|
||||||
lastChange && timestampOf(lastChange.dateTime) > timestampOf(lastRelevantManualChange.processedDate);
|
lastChange && timestampOf(lastChange.dateTime) > timestampOf(lastRelevantManualChange.processedDate);
|
||||||
|
|
||||||
if (lastChangeOccurredAfterLastManualChange && lastChange.type === ChangeTypes.ADDED && entry.redacted) {
|
if (
|
||||||
|
lastChangeOccurredAfterLastManualChange &&
|
||||||
|
lastChange.type === ChangeTypes.ADDED &&
|
||||||
|
logEntry.entryType === EntryTypes.ENTITY
|
||||||
|
) {
|
||||||
annotationWrapper.superType = SuperTypes.Redaction;
|
annotationWrapper.superType = SuperTypes.Redaction;
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
annotationWrapper.pending = !lastRelevantManualChange.processed;
|
annotationWrapper.pending = !lastRelevantManualChange.processed;
|
||||||
|
|
||||||
annotationWrapper.superType = this.#selectSuperType(entry, lastRelevantManualChange, annotationWrapper.hintDictionary);
|
annotationWrapper.superType = this.#selectSuperType(logEntry, lastRelevantManualChange, annotationWrapper.hintDictionary);
|
||||||
|
|
||||||
if (lastRelevantManualChange.annotationStatus === LogEntryStatuses.REQUESTED) {
|
if (lastRelevantManualChange.annotationStatus === LogEntryStatuses.REQUESTED) {
|
||||||
annotationWrapper.recategorizationType = lastRelevantManualChange.propertyChanges.type;
|
annotationWrapper.recategorizationType = lastRelevantManualChange.propertyChanges.type;
|
||||||
}
|
}
|
||||||
} else {
|
} else {
|
||||||
if (entry.recommendation) {
|
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;
|
annotationWrapper.superType = SuperTypes.Recommendation;
|
||||||
} else if (entry.redacted) {
|
} else if (logEntry.entryType === EntryTypes.ENTITY) {
|
||||||
annotationWrapper.superType = SuperTypes.Redaction;
|
annotationWrapper.superType = SuperTypes.Redaction;
|
||||||
} else if (entry.hint) {
|
} else if (logEntry.entryType === EntryTypes.HINT) {
|
||||||
annotationWrapper.superType = SuperTypes.Hint;
|
annotationWrapper.superType = SuperTypes.Hint;
|
||||||
} else {
|
} else {
|
||||||
annotationWrapper.superType = SuperTypes.Skipped;
|
annotationWrapper.superType = SuperTypes.Skipped;
|
||||||
@ -422,18 +439,18 @@ export class AnnotationWrapper implements IListable {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
static #createContent(annotationWrapper: AnnotationWrapper, entry: IRedactionLogEntry, isDocumine: boolean) {
|
static #createContent(annotationWrapper: AnnotationWrapper, logEntry: IEntityLogEntry, isDocumine: boolean) {
|
||||||
let content = '';
|
let content = '';
|
||||||
if (entry.matchedRule) {
|
if (logEntry.matchedRule) {
|
||||||
content += `Rule ${entry.matchedRule} matched${isDocumine ? ':' : ''} \n\n`;
|
content += `Rule ${logEntry.matchedRule} matched${isDocumine ? ':' : ''} \n\n`;
|
||||||
}
|
}
|
||||||
|
|
||||||
if (entry.reason) {
|
if (logEntry.reason) {
|
||||||
if (isDocumine && entry.reason.slice(-1) === '.') {
|
if (isDocumine && logEntry.reason.slice(-1) === '.') {
|
||||||
entry.reason = entry.reason.slice(0, -1);
|
logEntry.reason = logEntry.reason.slice(0, -1);
|
||||||
}
|
}
|
||||||
|
|
||||||
content += entry.reason + '\n\n';
|
content += logEntry.reason + '\n\n';
|
||||||
//remove leading and trailing commas and whitespaces
|
//remove leading and trailing commas and whitespaces
|
||||||
content = content.replace(/(^[, ]*)|([, ]*$)/g, '');
|
content = content.replace(/(^[, ]*)|([, ]*$)/g, '');
|
||||||
content = content.substring(0, 1).toUpperCase() + content.substring(1);
|
content = content.substring(0, 1).toUpperCase() + content.substring(1);
|
||||||
@ -447,12 +464,12 @@ export class AnnotationWrapper implements IListable {
|
|||||||
content += 'Removed by manual override';
|
content += 'Removed by manual override';
|
||||||
}
|
}
|
||||||
|
|
||||||
if (entry.section) {
|
if (logEntry.section) {
|
||||||
let prefix = `In section${isDocumine ? '' : ':'} `;
|
let prefix = `In section${isDocumine ? '' : ':'} `;
|
||||||
if (content.length) {
|
if (content.length) {
|
||||||
prefix = ` ${prefix.toLowerCase()}`;
|
prefix = ` ${prefix.toLowerCase()}`;
|
||||||
}
|
}
|
||||||
content += `${prefix} "${entry.section}"`;
|
content += `${prefix} "${logEntry.section}"`;
|
||||||
}
|
}
|
||||||
|
|
||||||
annotationWrapper.shortContent = this.#getShortContent(annotationWrapper) || content;
|
annotationWrapper.shortContent = this.#getShortContent(annotationWrapper) || content;
|
||||||
@ -472,7 +489,7 @@ export class AnnotationWrapper implements IListable {
|
|||||||
return annotationWrapper.legalBasis;
|
return annotationWrapper.legalBasis;
|
||||||
}
|
}
|
||||||
|
|
||||||
static #selectSuperType(redactionLogEntry: IRedactionLogEntry, lastManualChange: IManualChange, isHintDictionary: boolean): SuperType {
|
static #selectSuperType(logEntry: IEntityLogEntry, lastManualChange: IManualChange, isHintDictionary: boolean): SuperType {
|
||||||
switch (lastManualChange.manualRedactionType) {
|
switch (lastManualChange.manualRedactionType) {
|
||||||
case ManualRedactionTypes.ADD_LOCALLY:
|
case ManualRedactionTypes.ADD_LOCALLY:
|
||||||
switch (lastManualChange.annotationStatus) {
|
switch (lastManualChange.annotationStatus) {
|
||||||
@ -503,7 +520,7 @@ export class AnnotationWrapper implements IListable {
|
|||||||
return SuperTypes.Hint;
|
return SuperTypes.Hint;
|
||||||
}
|
}
|
||||||
|
|
||||||
if (redactionLogEntry.redacted) {
|
if (logEntry.entryType === EntryTypes.ENTITY) {
|
||||||
return SuperTypes.Redaction;
|
return SuperTypes.Redaction;
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -514,7 +531,7 @@ export class AnnotationWrapper implements IListable {
|
|||||||
}
|
}
|
||||||
break;
|
break;
|
||||||
case ManualRedactionTypes.REMOVE_FROM_DICTIONARY:
|
case ManualRedactionTypes.REMOVE_FROM_DICTIONARY:
|
||||||
if (redactionLogEntry.redacted) {
|
if (logEntry.entryType === EntryTypes.ENTITY) {
|
||||||
if (lastManualChange.processed) {
|
if (lastManualChange.processed) {
|
||||||
switch (lastManualChange.annotationStatus) {
|
switch (lastManualChange.annotationStatus) {
|
||||||
case LogEntryStatuses.APPROVED:
|
case LogEntryStatuses.APPROVED:
|
||||||
@ -524,35 +541,34 @@ export class AnnotationWrapper implements IListable {
|
|||||||
case LogEntryStatuses.REQUESTED:
|
case LogEntryStatuses.REQUESTED:
|
||||||
return SuperTypes.SuggestionRemoveDictionary;
|
return SuperTypes.SuggestionRemoveDictionary;
|
||||||
}
|
}
|
||||||
} else {
|
|
||||||
switch (lastManualChange.annotationStatus) {
|
|
||||||
case LogEntryStatuses.APPROVED:
|
|
||||||
case LogEntryStatuses.DECLINED:
|
|
||||||
return SuperTypes.Redaction;
|
|
||||||
case LogEntryStatuses.REQUESTED:
|
|
||||||
return SuperTypes.SuggestionRemoveDictionary;
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
} else {
|
switch (lastManualChange.annotationStatus) {
|
||||||
if (lastManualChange.processed) {
|
case LogEntryStatuses.APPROVED:
|
||||||
switch (lastManualChange.annotationStatus) {
|
case LogEntryStatuses.DECLINED:
|
||||||
case LogEntryStatuses.APPROVED:
|
return SuperTypes.Redaction;
|
||||||
return redactionLogEntry.recommendation ? SuperTypes.Recommendation : SuperTypes.Skipped;
|
case LogEntryStatuses.REQUESTED:
|
||||||
case LogEntryStatuses.DECLINED:
|
return SuperTypes.SuggestionRemoveDictionary;
|
||||||
return isHintDictionary ? SuperTypes.Hint : SuperTypes.Skipped;
|
|
||||||
case LogEntryStatuses.REQUESTED:
|
|
||||||
return SuperTypes.SuggestionRemoveDictionary;
|
|
||||||
}
|
|
||||||
} else {
|
|
||||||
switch (lastManualChange.annotationStatus) {
|
|
||||||
case LogEntryStatuses.APPROVED:
|
|
||||||
case LogEntryStatuses.DECLINED:
|
|
||||||
return isHintDictionary ? SuperTypes.Hint : SuperTypes.Skipped;
|
|
||||||
case LogEntryStatuses.REQUESTED:
|
|
||||||
return SuperTypes.SuggestionRemoveDictionary;
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
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;
|
||||||
|
case LogEntryStatuses.REQUESTED:
|
||||||
|
return SuperTypes.SuggestionRemoveDictionary;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
switch (lastManualChange.annotationStatus) {
|
||||||
|
case LogEntryStatuses.APPROVED:
|
||||||
|
case LogEntryStatuses.DECLINED:
|
||||||
|
return isHintDictionary ? SuperTypes.Hint : SuperTypes.Skipped;
|
||||||
|
case LogEntryStatuses.REQUESTED:
|
||||||
|
return SuperTypes.SuggestionRemoveDictionary;
|
||||||
|
}
|
||||||
break;
|
break;
|
||||||
case ManualRedactionTypes.FORCE_REDACT:
|
case ManualRedactionTypes.FORCE_REDACT:
|
||||||
switch (lastManualChange.annotationStatus) {
|
switch (lastManualChange.annotationStatus) {
|
||||||
@ -578,13 +594,18 @@ export class AnnotationWrapper implements IListable {
|
|||||||
switch (lastManualChange.annotationStatus) {
|
switch (lastManualChange.annotationStatus) {
|
||||||
case LogEntryStatuses.APPROVED:
|
case LogEntryStatuses.APPROVED:
|
||||||
case LogEntryStatuses.DECLINED: {
|
case LogEntryStatuses.DECLINED: {
|
||||||
if (redactionLogEntry.recommendation) {
|
if (logEntry.entryType === EntryTypes.RECOMMENDATION) {
|
||||||
return SuperTypes.Recommendation;
|
return SuperTypes.Recommendation;
|
||||||
} else if (redactionLogEntry.redacted) {
|
}
|
||||||
|
|
||||||
|
if (logEntry.entryType === EntryTypes.ENTITY) {
|
||||||
return SuperTypes.Redaction;
|
return SuperTypes.Redaction;
|
||||||
} else if (redactionLogEntry.hint) {
|
}
|
||||||
|
|
||||||
|
if (logEntry.entryType === EntryTypes.HINT) {
|
||||||
return SuperTypes.Hint;
|
return SuperTypes.Hint;
|
||||||
}
|
}
|
||||||
|
|
||||||
return isHintDictionary ? SuperTypes.IgnoredHint : SuperTypes.Skipped;
|
return isHintDictionary ? SuperTypes.IgnoredHint : SuperTypes.Skipped;
|
||||||
}
|
}
|
||||||
case LogEntryStatuses.REQUESTED:
|
case LogEntryStatuses.REQUESTED:
|
||||||
@ -595,7 +616,7 @@ export class AnnotationWrapper implements IListable {
|
|||||||
switch (lastManualChange.annotationStatus) {
|
switch (lastManualChange.annotationStatus) {
|
||||||
case LogEntryStatuses.APPROVED:
|
case LogEntryStatuses.APPROVED:
|
||||||
case LogEntryStatuses.DECLINED:
|
case LogEntryStatuses.DECLINED:
|
||||||
return redactionLogEntry.type === SuperTypes.ManualRedaction ? SuperTypes.ManualRedaction : SuperTypes.Redaction;
|
return logEntry.type === SuperTypes.ManualRedaction ? SuperTypes.ManualRedaction : SuperTypes.Redaction;
|
||||||
case LogEntryStatuses.REQUESTED:
|
case LogEntryStatuses.REQUESTED:
|
||||||
return SuperTypes.SuggestionChangeLegalBasis;
|
return SuperTypes.SuggestionChangeLegalBasis;
|
||||||
}
|
}
|
||||||
@ -604,17 +625,18 @@ export class AnnotationWrapper implements IListable {
|
|||||||
switch (lastManualChange.annotationStatus) {
|
switch (lastManualChange.annotationStatus) {
|
||||||
case LogEntryStatuses.APPROVED:
|
case LogEntryStatuses.APPROVED:
|
||||||
case LogEntryStatuses.DECLINED:
|
case LogEntryStatuses.DECLINED:
|
||||||
if (redactionLogEntry.recommendation) {
|
if (logEntry.entryType === EntryTypes.RECOMMENDATION) {
|
||||||
return SuperTypes.Recommendation;
|
return SuperTypes.Recommendation;
|
||||||
} else if (redactionLogEntry.redacted) {
|
}
|
||||||
return redactionLogEntry.type === SuperTypes.ManualRedaction
|
|
||||||
? SuperTypes.ManualRedaction
|
if (logEntry.entryType === EntryTypes.ENTITY) {
|
||||||
: SuperTypes.Redaction;
|
return logEntry.type === SuperTypes.ManualRedaction ? SuperTypes.ManualRedaction : SuperTypes.Redaction;
|
||||||
} else if (redactionLogEntry.hint) {
|
}
|
||||||
|
|
||||||
|
if (logEntry.entryType === EntryTypes.HINT) {
|
||||||
return SuperTypes.Hint;
|
return SuperTypes.Hint;
|
||||||
}
|
}
|
||||||
return isHintDictionary ? SuperTypes.IgnoredHint : SuperTypes.Skipped;
|
return isHintDictionary ? SuperTypes.IgnoredHint : SuperTypes.Skipped;
|
||||||
|
|
||||||
case LogEntryStatuses.REQUESTED:
|
case LogEntryStatuses.REQUESTED:
|
||||||
return SuperTypes.SuggestionResize;
|
return SuperTypes.SuggestionResize;
|
||||||
}
|
}
|
||||||
|
|||||||
@ -3,17 +3,7 @@ import { toObservable } from '@angular/core/rxjs-interop';
|
|||||||
import { marker as _ } from '@biesbjerg/ngx-translate-extract-marker';
|
import { marker as _ } from '@biesbjerg/ngx-translate-extract-marker';
|
||||||
import { EntitiesService, getConfig, Toaster } from '@iqser/common-ui';
|
import { EntitiesService, getConfig, Toaster } from '@iqser/common-ui';
|
||||||
import { AnnotationWrapper } from '@models/file/annotation.wrapper';
|
import { AnnotationWrapper } from '@models/file/annotation.wrapper';
|
||||||
import {
|
import { ChangeType, ChangeTypes, File, IEntityLog, IEntityLogEntry, LogEntryStatuses, ViewedPage, ViewMode, ViewModes } from '@red/domain';
|
||||||
ChangeType,
|
|
||||||
ChangeTypes,
|
|
||||||
File,
|
|
||||||
IRedactionLog,
|
|
||||||
IRedactionLogEntry,
|
|
||||||
LogEntryStatuses,
|
|
||||||
ViewedPage,
|
|
||||||
ViewMode,
|
|
||||||
ViewModes,
|
|
||||||
} from '@red/domain';
|
|
||||||
import { DefaultColorsService } from '@services/entity-services/default-colors.service';
|
import { DefaultColorsService } from '@services/entity-services/default-colors.service';
|
||||||
import { DictionaryService } from '@services/entity-services/dictionary.service';
|
import { DictionaryService } from '@services/entity-services/dictionary.service';
|
||||||
import { EarmarksService } from '@services/files/earmarks.service';
|
import { EarmarksService } from '@services/files/earmarks.service';
|
||||||
@ -132,7 +122,7 @@ export class FileDataService extends EntitiesService<AnnotationWrapper, Annotati
|
|||||||
|
|
||||||
async loadRedactionLog() {
|
async loadRedactionLog() {
|
||||||
this._logger.info('[REDACTION_LOG] Loading redaction log...');
|
this._logger.info('[REDACTION_LOG] Loading redaction log...');
|
||||||
const redactionLog = await this._redactionLogService.getRedactionLog(this._state.dossierId, this._state.fileId);
|
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);
|
let annotations = await this.#convertData(redactionLog);
|
||||||
@ -177,8 +167,8 @@ export class FileDataService extends EntitiesService<AnnotationWrapper, Annotati
|
|||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
async #convertData(redactionLog: IRedactionLog) {
|
async #convertData(entityLog: IEntityLog) {
|
||||||
if (!redactionLog.redactionLogEntry) {
|
if (!entityLog.entityLogEntry) {
|
||||||
return [];
|
return [];
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -189,8 +179,8 @@ export class FileDataService extends EntitiesService<AnnotationWrapper, Annotati
|
|||||||
const defaultColors = this._defaultColorsService.find(this._state.dossierTemplateId);
|
const defaultColors = this._defaultColorsService.find(this._state.dossierTemplateId);
|
||||||
let checkDictionary = true;
|
let checkDictionary = true;
|
||||||
|
|
||||||
for (const entry of redactionLog.redactionLogEntry) {
|
for (const entry of entityLog.entityLogEntry) {
|
||||||
const pageNumber = entry.positions[0]?.page;
|
const pageNumber = entry.positions[0].pageNumber;
|
||||||
const manual = entry.manualChanges?.length > 0;
|
const manual = entry.manualChanges?.length > 0;
|
||||||
if (!manual && file.excludedPages.includes(pageNumber)) {
|
if (!manual && file.excludedPages.includes(pageNumber)) {
|
||||||
continue;
|
continue;
|
||||||
@ -219,17 +209,17 @@ export class FileDataService extends EntitiesService<AnnotationWrapper, Annotati
|
|||||||
dictionaries,
|
dictionaries,
|
||||||
defaultColors,
|
defaultColors,
|
||||||
changeLogValues.changeLogType,
|
changeLogValues.changeLogType,
|
||||||
redactionLog.legalBasis ?? [],
|
entityLog.legalBasis ?? [],
|
||||||
!!dictionary?.hint,
|
!!dictionary?.hint,
|
||||||
this.#isDocumine,
|
this.#isDocumine,
|
||||||
);
|
);
|
||||||
|
|
||||||
if (entry.sourceId) {
|
// if (entry.sourceId) {
|
||||||
if (!sourceIds[entry.sourceId]) {
|
// if (!sourceIds[entry.sourceId]) {
|
||||||
sourceIds[entry.sourceId] = [];
|
// sourceIds[entry.sourceId] = [];
|
||||||
}
|
// }
|
||||||
sourceIds[entry.sourceId].push(annotation);
|
// sourceIds[entry.sourceId].push(annotation);
|
||||||
}
|
// }
|
||||||
|
|
||||||
annotations.push(annotation);
|
annotations.push(annotation);
|
||||||
}
|
}
|
||||||
@ -239,13 +229,13 @@ export class FileDataService extends EntitiesService<AnnotationWrapper, Annotati
|
|||||||
}
|
}
|
||||||
|
|
||||||
#getChangeLogValues(
|
#getChangeLogValues(
|
||||||
redactionLogEntry: IRedactionLogEntry,
|
entityLogEntry: IEntityLogEntry,
|
||||||
file: File,
|
file: File,
|
||||||
): {
|
): {
|
||||||
hidden: boolean;
|
hidden: boolean;
|
||||||
changeLogType?: ChangeType;
|
changeLogType?: ChangeType;
|
||||||
} {
|
} {
|
||||||
const hasManualChanges = redactionLogEntry.manualChanges?.length > 0;
|
const hasManualChanges = entityLogEntry.manualChanges?.length > 0;
|
||||||
if (file.numberOfAnalyses <= 1 && !file.hasUpdates && !hasManualChanges) {
|
if (file.numberOfAnalyses <= 1 && !file.hasUpdates && !hasManualChanges) {
|
||||||
return {
|
return {
|
||||||
changeLogType: undefined,
|
changeLogType: undefined,
|
||||||
@ -253,10 +243,10 @@ export class FileDataService extends EntitiesService<AnnotationWrapper, Annotati
|
|||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
|
||||||
const viableChanges = redactionLogEntry.changes?.filter(c => c.analysisNumber > 1);
|
const viableChanges = entityLogEntry.changes?.filter(c => c.analysisNumber > 1);
|
||||||
const lastChange = viableChanges?.sort(chronologicallyBy(x => x.dateTime)).at(-1);
|
const lastChange = viableChanges?.sort(chronologicallyBy(x => x.dateTime)).at(-1);
|
||||||
|
|
||||||
const page = redactionLogEntry.positions?.[0].page;
|
const page = entityLogEntry.positions?.[0].pageNumber;
|
||||||
const viewedPage = this.#originalViewedPages.find(p => p.page === page);
|
const viewedPage = this.#originalViewedPages.find(p => p.page === page);
|
||||||
|
|
||||||
if (!viewedPage) {
|
if (!viewedPage) {
|
||||||
@ -279,7 +269,7 @@ export class FileDataService extends EntitiesService<AnnotationWrapper, Annotati
|
|||||||
}
|
}
|
||||||
|
|
||||||
const visibleStatuses: string[] = [LogEntryStatuses.APPROVED, LogEntryStatuses.REQUESTED];
|
const visibleStatuses: string[] = [LogEntryStatuses.APPROVED, LogEntryStatuses.REQUESTED];
|
||||||
const viableManualChanges = redactionLogEntry.manualChanges.filter(change => visibleStatuses.includes(change.annotationStatus));
|
const viableManualChanges = entityLogEntry.manualChanges.filter(change => visibleStatuses.includes(change.annotationStatus));
|
||||||
viableManualChanges.sort(chronologicallyBy(x => x.processedDate));
|
viableManualChanges.sort(chronologicallyBy(x => x.processedDate));
|
||||||
const lastManualChange = viableManualChanges.at(-1);
|
const lastManualChange = viableManualChanges.at(-1);
|
||||||
const isSuggestion = lastManualChange?.annotationStatus === LogEntryStatuses.REQUESTED;
|
const isSuggestion = lastManualChange?.annotationStatus === LogEntryStatuses.REQUESTED;
|
||||||
|
|||||||
@ -1,8 +1,8 @@
|
|||||||
import { Injectable } from '@angular/core';
|
import { Injectable } from '@angular/core';
|
||||||
import { GenericService, QueryParam } from '@iqser/common-ui';
|
import { GenericService } from '@iqser/common-ui';
|
||||||
import { IRedactionLog, ISectionGrid } from '@red/domain';
|
import { IEntityLog, ISectionGrid } from '@red/domain';
|
||||||
import { catchError } from 'rxjs/operators';
|
|
||||||
import { firstValueFrom, of } from 'rxjs';
|
import { firstValueFrom, of } from 'rxjs';
|
||||||
|
import { catchError } from 'rxjs/operators';
|
||||||
|
|
||||||
@Injectable({
|
@Injectable({
|
||||||
providedIn: 'root',
|
providedIn: 'root',
|
||||||
@ -10,16 +10,11 @@ import { firstValueFrom, of } from 'rxjs';
|
|||||||
export class RedactionLogService extends GenericService<unknown> {
|
export class RedactionLogService extends GenericService<unknown> {
|
||||||
protected readonly _defaultModelPath = '';
|
protected readonly _defaultModelPath = '';
|
||||||
|
|
||||||
async getRedactionLog(dossierId: string, fileId: string, withManualRedactions?: boolean) {
|
async getEntityLog(dossierId: string, fileId: string) {
|
||||||
const queryParams: QueryParam[] = [];
|
const entityLog$ = this._getOne<IEntityLog>([dossierId, fileId], 'entityLog');
|
||||||
if (withManualRedactions) {
|
const entityLog = await firstValueFrom(entityLog$.pipe(catchError(() => of({} as IEntityLog))));
|
||||||
queryParams.push({ key: 'withManualRedactions', value: withManualRedactions });
|
entityLog.entityLogEntry.sort((a, b) => a.positions[0].pageNumber - b.positions[0].pageNumber);
|
||||||
}
|
return entityLog;
|
||||||
|
|
||||||
const redactionLog$ = this._getOne<IRedactionLog>([dossierId, fileId], 'redactionLog', queryParams);
|
|
||||||
const redactionLog = await firstValueFrom(redactionLog$.pipe(catchError(() => of({} as IRedactionLog))));
|
|
||||||
redactionLog.redactionLogEntry.sort((a, b) => a.positions[0].page - b.positions[0].page);
|
|
||||||
return redactionLog;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
getSectionGrid(dossierId: string, fileId: string) {
|
getSectionGrid(dossierId: string, fileId: string) {
|
||||||
|
|||||||
@ -1 +1 @@
|
|||||||
Subproject commit a6383c1dbc840115897a31567c3f5633ba78b43a
|
Subproject commit 85fba4a1dd57ecbb133d938552d75fceffa1099c
|
||||||
@ -1,5 +1,5 @@
|
|||||||
export interface ILegalBasis {
|
export interface ILegalBasis {
|
||||||
name: string;
|
readonly name: string;
|
||||||
description?: string;
|
readonly description?: string;
|
||||||
reason?: string;
|
readonly reason?: string;
|
||||||
}
|
}
|
||||||
|
|||||||
40
libs/red-domain/src/lib/redaction-log/entity-log-entry.ts
Normal file
40
libs/red-domain/src/lib/redaction-log/entity-log-entry.ts
Normal file
@ -0,0 +1,40 @@
|
|||||||
|
import { ITrackable } from '@iqser/common-ui';
|
||||||
|
import { IChange } from './change';
|
||||||
|
import { EntryState } from './entity-states';
|
||||||
|
import { EntryType } from './entry-types';
|
||||||
|
import { IManualChange } from './manual-change';
|
||||||
|
import { LogEntryEngine } from './types';
|
||||||
|
|
||||||
|
export interface IEntityLogEntryPosition {
|
||||||
|
readonly pageNumber: number;
|
||||||
|
readonly rectangle: readonly [number, number, number, number];
|
||||||
|
}
|
||||||
|
|
||||||
|
export interface IEntityLogEntry extends ITrackable {
|
||||||
|
type: string;
|
||||||
|
entryType: EntryType;
|
||||||
|
state: EntryState;
|
||||||
|
value: string;
|
||||||
|
reason: string;
|
||||||
|
matchedRule: string;
|
||||||
|
legalBasis: string;
|
||||||
|
imported: boolean;
|
||||||
|
containingNodeId: number[];
|
||||||
|
closestHeadline: string;
|
||||||
|
section: string;
|
||||||
|
color: number[];
|
||||||
|
positions: IEntityLogEntryPosition[];
|
||||||
|
textAfter?: string;
|
||||||
|
textBefore?: string;
|
||||||
|
startOffset?: number;
|
||||||
|
endOffset?: number;
|
||||||
|
imageHasTransparency: boolean;
|
||||||
|
dictionaryEntry: boolean;
|
||||||
|
dossierDictionaryEntry: boolean;
|
||||||
|
excluded: boolean;
|
||||||
|
changes: IChange[];
|
||||||
|
manualChanges: IManualChange[];
|
||||||
|
engines: LogEntryEngine[];
|
||||||
|
reference: string[];
|
||||||
|
importedRedactionIntersections: string[];
|
||||||
|
}
|
||||||
13
libs/red-domain/src/lib/redaction-log/entity-log.ts
Normal file
13
libs/red-domain/src/lib/redaction-log/entity-log.ts
Normal file
@ -0,0 +1,13 @@
|
|||||||
|
import { ILegalBasis } from '../legal-basis';
|
||||||
|
import { IEntityLogEntry } from './entity-log-entry';
|
||||||
|
|
||||||
|
export interface IEntityLog {
|
||||||
|
analysisVersion: number;
|
||||||
|
analysisNumber: number;
|
||||||
|
entityLogEntry: IEntityLogEntry[];
|
||||||
|
legalBasis: ILegalBasis[];
|
||||||
|
dictionaryVersion: number;
|
||||||
|
dossierDictionaryVersion: number;
|
||||||
|
rulesVersion: number;
|
||||||
|
legalBasisVersion: number;
|
||||||
|
}
|
||||||
8
libs/red-domain/src/lib/redaction-log/entity-states.ts
Normal file
8
libs/red-domain/src/lib/redaction-log/entity-states.ts
Normal file
@ -0,0 +1,8 @@
|
|||||||
|
export const EntryStates = {
|
||||||
|
APPLIED: 'APPLIED',
|
||||||
|
SKIPPED: 'SKIPPED',
|
||||||
|
IGNORED: 'IGNORED',
|
||||||
|
REMOVED: 'REMOVED',
|
||||||
|
} as const;
|
||||||
|
|
||||||
|
export type EntryState = (typeof EntryStates)[keyof typeof EntryStates];
|
||||||
11
libs/red-domain/src/lib/redaction-log/entry-types.ts
Normal file
11
libs/red-domain/src/lib/redaction-log/entry-types.ts
Normal file
@ -0,0 +1,11 @@
|
|||||||
|
export const EntryTypes = {
|
||||||
|
ENTITY: 'ENTITY',
|
||||||
|
HINT: 'HINT',
|
||||||
|
FALSE_POSITIVE: 'FALSE_POSITIVE',
|
||||||
|
RECOMMENDATION: 'RECOMMENDATION',
|
||||||
|
FALSE_RECOMMENDATION: 'FALSE_RECOMMENDATION',
|
||||||
|
AREA: 'AREA',
|
||||||
|
IMAGE: 'IMAGE',
|
||||||
|
} as const;
|
||||||
|
|
||||||
|
export type EntryType = (typeof EntryTypes)[keyof typeof EntryTypes];
|
||||||
@ -12,3 +12,7 @@ export * from './recategorization.request';
|
|||||||
export * from './resize.request';
|
export * from './resize.request';
|
||||||
export * from './manual-change';
|
export * from './manual-change';
|
||||||
export * from './dictionary-entry-types';
|
export * from './dictionary-entry-types';
|
||||||
|
export * from './entity-log';
|
||||||
|
export * from './entity-log-entry';
|
||||||
|
export * from './entity-states';
|
||||||
|
export * from './entry-types';
|
||||||
|
|||||||
Loading…
x
Reference in New Issue
Block a user