Merge branch 'master' into RED-8904

This commit is contained in:
Nicoleta Panaghiu 2024-04-10 16:29:02 +03:00
commit 8a37009790
12 changed files with 124 additions and 113 deletions

View File

@ -17,7 +17,7 @@ interface SyntaxError {
line: number;
column: number;
message: string;
warning: boolean;
warning?: boolean;
}
interface UploadResponse {
@ -43,6 +43,7 @@ export class RulesScreenComponent implements OnInit, ComponentCanDeactivate {
automaticLayout: true,
readOnly: !this.permissionsService.canEditRules(),
glyphMargin: true,
fixedOverflowWidgets: true,
};
initialLines: string[] = [];
currentLines: string[] = [];
@ -151,10 +152,18 @@ export class RulesScreenComponent implements OnInit, ComponentCanDeactivate {
}
},
error => {
const errors = this.#mapErrors(error.error);
let errors: SyntaxError[];
if (error.error?.syntaxErrorMessages) {
errors = this.#mapErrors(error.error);
} else {
const syntaxError: SyntaxError = { message: error.error.message, line: 1, column: 0 };
errors = this.#mapErrors({ blacklistErrorMessages: [], syntaxErrorMessages: [syntaxError], deprecatedWarnings: [] });
}
this.#drawErrorMarkers(errors);
this._loadingService.stop();
this._toaster.error(rulesScreenTranslations[this.type]['error.generic']);
if (!dryRun) {
this._toaster.error(rulesScreenTranslations[this.type]['error.generic']);
}
},
);
}
@ -252,6 +261,7 @@ export class RulesScreenComponent implements OnInit, ComponentCanDeactivate {
},
});
});
this.#errors.set(errors);
this.#errorGlyphs.set(this.#codeEditor.deltaDecorations(this.#errorGlyphs(), glyphs));
(window as any).monaco.editor.setModelMarkers(model, model.id, markers);

View File

@ -93,6 +93,11 @@ export class AnnotationDetailsComponent implements OnChanges {
description: _('annotation-engines.imported'),
show: isBasedOn(annotation, Engines.IMPORTED),
},
{
icon: 'red:redaction-changes',
description: _('annotation-engines.manual'),
show: isBasedOn(annotation, Engines.MANUAL),
},
];
}
}

View File

@ -16,7 +16,7 @@ import {
import { CommentsApiService } from '@services/comments-api.service';
import { DossierTemplatesService } from '@services/dossier-templates/dossier-templates.service';
import { PermissionsService } from '@services/permissions.service';
import { firstValueFrom, Observable, zip } from 'rxjs';
import { firstValueFrom, Observable } from 'rxjs';
import { getFirstRelevantTextPart } from '../../../utils';
import { AnnotationDrawService } from '../../pdf-viewer/services/annotation-draw.service';
import { REDAnnotationManager } from '../../pdf-viewer/services/annotation-manager.service';
@ -32,6 +32,7 @@ import { ResizeRedactionDialogComponent } from '../dialogs/resize-redaction-dial
import { RemoveRedactionOptions } from '../utils/dialog-options';
import {
EditRedactionData,
EditRedactResult,
RemoveRedactionData,
RemoveRedactionPermissions,
RemoveRedactionResult,
@ -101,47 +102,34 @@ export class AnnotationActionsService {
};
const result = await this.#getEditRedactionDialog(data).result();
const requests: Observable<unknown>[] = [];
if (!result) {
return;
}
if (
!this.#isDocumine &&
(!annotations.every(annotation => annotation.legalBasis === result.legalBasis) ||
!annotations.every(annotation => annotation.section === result.section))
) {
const changeLegalBasisBody = annotations.map(annotation => ({
annotationId: annotation.id,
legalBasis: result.legalBasis,
section: result.section ?? annotation.section,
value: result.value ?? annotation.value,
}));
requests.push(
this._manualRedactionService.changeLegalBasis(
changeLegalBasisBody,
dossierId,
fileId,
file().excludedFromAutomaticAnalysis && isUnprocessed,
),
);
}
if (result.type && !annotations.every(annotation => annotation.type === result.type)) {
const recategorizeBody: List<IRecategorizationRequest> = annotations.map(annotation => ({
annotationId: annotation.id,
type: result.type ?? annotation.type,
}));
requests.push(
this._manualRedactionService.recategorizeRedactions(
const recategorizeBody: List<IRecategorizationRequest> = annotations.map(annotation => {
const body = { annotationId: annotation.id, type: result.type ?? annotation.type };
if (!this.#isDocumine) {
return {
...body,
legalBasis: result.legalBasis,
section: result.section ?? annotation.section,
value: result.value ?? annotation.value,
};
}
return body;
});
await this.#processObsAndEmit(
this._manualRedactionService
.recategorizeRedactions(
recategorizeBody,
dossierId,
fileId,
this.#getChangedFields(annotations, result),
file().excludedFromAutomaticAnalysis && isUnprocessed,
),
);
}
)
.pipe(log()),
);
if (result.comment) {
try {
@ -152,11 +140,6 @@ export class AnnotationActionsService {
this._toaster.rawError(error.error.message);
}
}
if (!requests.length) {
return;
}
await this.#processObsAndEmit(zip(requests).pipe(log()));
}
async removeRedaction(redactions: AnnotationWrapper[], permissions: AnnotationPermissions) {
@ -498,4 +481,23 @@ export class AnnotationActionsService {
isApprover,
};
}
#getChangedFields(annotations: AnnotationWrapper[], result: EditRedactResult) {
const changedFields = [];
if (result.type && !annotations.every(annotation => annotation.type === result.type)) {
changedFields.push('type');
}
if (this.#isDocumine) {
return { changes: changedFields.join(', ') };
}
if (result.legalBasis && !annotations.every(annotation => annotation.legalBasis === result.legalBasis)) {
changedFields.push('reason');
}
if (typeof result.section === 'string' && !annotations.every(annotation => annotation.section === result.section)) {
changedFields.push('paragraph/location');
}
return { changes: changedFields.join(', ') };
}
}

View File

@ -18,7 +18,7 @@ import type {
import { dictionaryActionsTranslations, manualRedactionActionsTranslations } from '@translations/annotation-actions-translations';
import { Roles } from '@users/roles';
import { NGXLogger } from 'ngx-logger';
import { EMPTY, of, OperatorFunction, pipe } from 'rxjs';
import { EMPTY, of, pipe } from 'rxjs';
import { catchError, tap } from 'rxjs/operators';
function getResponseType(error: boolean, isConflict: boolean) {
@ -62,12 +62,18 @@ export class ManualRedactionService extends GenericService<IManualAddResponse> {
return this.addAnnotation(recommendations, dossierId, fileId);
}
changeLegalBasis(body: List<ILegalBasisChangeRequest>, dossierId: string, fileId: string, includeUnprocessed = false) {
return this.legalBasisChange(body, dossierId, fileId, includeUnprocessed).pipe(this.#showToast('change-legal-basis'));
}
recategorizeRedactions(body: List<IRecategorizationRequest>, dossierId: string, fileId: string, includeUnprocessed = false) {
return this.recategorize(body, dossierId, fileId, includeUnprocessed).pipe(this.#showToast('change-type'));
recategorizeRedactions(
body: List<IRecategorizationRequest>,
dossierId: string,
fileId: string,
successMessageParameters?: {
[key: string]: string;
},
includeUnprocessed = false,
) {
return this.recategorize(body, dossierId, fileId, includeUnprocessed).pipe(
this.#showToast('recategorize-annotation', false, successMessageParameters),
);
}
addAnnotation(
@ -131,13 +137,6 @@ export class ManualRedactionService extends GenericService<IManualAddResponse> {
);
}
legalBasisChange(body: List<ILegalBasisChangeRequest>, dossierId: string, fileId: string, includeUnprocessed = false) {
return this._post(
body,
`${this.#bulkRedaction}/legalBasisChange/${dossierId}/${fileId}?includeUnprocessed=${includeUnprocessed}`,
).pipe(this.#log('Legal basis change', body));
}
undo(annotationIds: List, dossierId: string, fileId: string) {
const url = `${this._defaultModelPath}/bulk/undo/${dossierId}/${fileId}`;
return super.delete(annotationIds, url).pipe(this.#log('Undo', annotationIds));
@ -165,7 +164,11 @@ export class ManualRedactionService extends GenericService<IManualAddResponse> {
});
}
#showToast(action: ManualRedactionActions | DictionaryActions, isDictionary = false) {
#showToast(
action: ManualRedactionActions | DictionaryActions,
isDictionary = false,
successMessageParameters?: { [key: string]: string },
) {
return pipe(
catchError((error: unknown) => {
const isConflict = (error as HttpErrorResponse).status === HttpStatusCode.Conflict;
@ -175,7 +178,12 @@ export class ManualRedactionService extends GenericService<IManualAddResponse> {
});
return EMPTY;
}),
tap(() => this._toaster.success(getMessage(action, isDictionary), { positionClass: 'toast-file-preview' })),
tap(() =>
this._toaster.success(getMessage(action, isDictionary), {
params: successMessageParameters,
positionClass: 'toast-file-preview',
}),
),
);
}

View File

@ -26,7 +26,7 @@ export class EntityLogService extends GenericService<unknown> {
#filterInvalidEntries(entityLogEntry: IEntityLogEntry[]) {
return entityLogEntry.filter(entry => {
entry.positions = entry.positions.filter(p => !!p.rectangle?.length);
entry.positions = entry.positions?.filter(p => !!p.rectangle?.length);
const hasPositions = !!entry.positions?.length;
const isRemoved = entry.state === EntryStates.REMOVED;
if (!hasPositions) {

View File

@ -28,10 +28,6 @@ export const manualRedactionActionsTranslations: Record<ManualRedactionActions,
error: _('annotation-actions.message.manual-redaction.add.error'),
success: _('annotation-actions.message.manual-redaction.add.success'),
},
'change-legal-basis': {
error: _('annotation-actions.message.manual-redaction.change-legal-basis.error'),
success: _('annotation-actions.message.manual-redaction.change-legal-basis.success'),
},
'force-redaction': {
error: _('annotation-actions.message.manual-redaction.force-redaction.error'),
success: _('annotation-actions.message.manual-redaction.force-redaction.success'),
@ -44,9 +40,9 @@ export const manualRedactionActionsTranslations: Record<ManualRedactionActions,
error: _('annotation-actions.message.manual-redaction.recategorize-image.error'),
success: _('annotation-actions.message.manual-redaction.recategorize-image.success'),
},
'change-type': {
error: _('annotation-actions.message.manual-redaction.change-type.error'),
success: _('annotation-actions.message.manual-redaction.change-type.success'),
'recategorize-annotation': {
error: _('annotation-actions.message.manual-redaction.recategorize-annotation.error'),
success: _('annotation-actions.message.manual-redaction.recategorize-annotation.success'),
},
undo: {
error: _('annotation-actions.message.manual-redaction.undo.error'),

View File

@ -288,14 +288,6 @@
"error": "Fehler beim Speichern der Schwärzung: {error}",
"success": "Schwärzung hinzugefügt!"
},
"change-legal-basis": {
"error": "Fehler beim Bearbeiten der in der Anmerkung genannten Begründung: {error}",
"success": "In der Anmerkung genannte Begründung wurde bearbeitet."
},
"change-type": {
"error": "Failed to edit type: {error}",
"success": "Type was edited."
},
"force-hint": {
"error": "Failed to save hint: {error}",
"success": "Hint added!"
@ -304,6 +296,10 @@
"error": "Die Schwärzung konnte nicht gespeichert werden!",
"success": "Schwärzung eingefügt!"
},
"recategorize-annotation": {
"error": "",
"success": ""
},
"recategorize-image": {
"error": "Rekategorisierung des Bildes gescheitert: {error}",
"success": "Bild wurde einer neuen Kategorie zugeordnet."
@ -355,6 +351,7 @@
"annotation-engines": {
"dictionary": "{isHint, select, true{Hint} other{Redaction}} basierend auf Wörterbuch",
"imported": "Imported",
"manual": "",
"ner": "Redaktion basierend auf KI",
"rule": "Schwärzung basierend auf Regel {rule}"
},

View File

@ -270,7 +270,7 @@
"message": {
"dictionary": {
"add": {
"conflict-error": "Cannot add ''{content}'' to the {dictionaryName} dictionary because it was recognized as a general term that appears too often in texts.",
"conflict-error": "Cannot add '{content}' to the {dictionaryName} dictionary because it was recognized as a general term that appears too often in texts.",
"error": "Failed to add entry to dictionary: {error}",
"success": "Entry added to dictionary. Changes will be visible after reanalysis."
},
@ -288,14 +288,6 @@
"error": "Failed to save redaction: {error}",
"success": "Redaction added!"
},
"change-legal-basis": {
"error": "Failed to edit annotation reason: {error}",
"success": "Annotation reason was edited."
},
"change-type": {
"error": "Failed to edit type: {error}",
"success": "Type was edited."
},
"force-hint": {
"error": "Failed to save hint: {error}",
"success": "Hint added!"
@ -304,6 +296,10 @@
"error": "Failed to save redaction: {error}",
"success": "Redaction added!"
},
"recategorize-annotation": {
"error": "Failed to edit type: {error}",
"success": "Annotation was edited: Changed {changes}."
},
"recategorize-image": {
"error": "Failed to recategorize image: {error}",
"success": "Image recategorized."
@ -355,6 +351,7 @@
"annotation-engines": {
"dictionary": "Based on dictionary",
"imported": "Imported",
"manual": "Manual",
"ner": "Based on AI",
"rule": "Based on rule"
},
@ -1255,7 +1252,7 @@
},
"title": "Entity rule editor",
"warning-text": "Warning: experimental feature!",
"warnings-found": "{warnings, plural, one{A warning} other{{warnings} warnings}} found in rules"
"warnings-found": ""
},
"entity": {
"info": {
@ -1491,7 +1488,7 @@
},
"is-excluded": "Redaction is disabled for this document.",
"multi-select": {
"close": "Close bulk select"
"close": ""
}
},
"text-highlights": "Earmarks",
@ -1499,7 +1496,7 @@
"toggle-analysis": {
"disable": "Disable redaction",
"enable": "Enable for redaction",
"only-managers": "Enable/disable redaction: assigned user only"
"only-managers": "Enable/disable: assigned user only"
}
},
"file-status": {
@ -1515,7 +1512,7 @@
"ner-analyzing": "NER analyzing",
"new": "New",
"ocr-processing": "OCR processing",
"processed": "processed",
"processed": "Processed",
"processing": "Processing...",
"re-processing": "Reprocessing...",
"reprocess": "Processing",
@ -1523,7 +1520,7 @@
"unassigned": "Unassigned",
"under-approval": "Under approval",
"under-review": "Under review",
"unprocessed": "unprocessed"
"unprocessed": "Unprocessed"
},
"file-upload": {
"type": {
@ -1957,7 +1954,7 @@
"processing-status": {
"ocr": "OCR",
"pending": "Pending",
"processed": "processed",
"processed": "Processed",
"processing": "Processing"
},
"processing": {
@ -2017,13 +2014,13 @@
"label": "Remove from dossier in this context"
},
"in-dossier": {
"description": "Do not annotate the term in this dossier.",
"description": "Do not annotate ''{value}'' as ''{type}'' in any dossier.",
"description-bulk": "",
"label": "Do not annotate as <i>{type}</i>",
"label": "No longer annotate as ''{type}''",
"label-bulk": ""
},
"only-here": {
"description": "Do not annotate the term at this position in the current document.",
"description": "Do not annotate ''{value}'' at this position in the current document.",
"description-bulk": "Do not auto-annotate the selected terms at this position in the current document.",
"label": "Remove here"
}
@ -2044,8 +2041,8 @@
"comment-placeholder": "Add remarks or mentions...",
"options": {
"do-not-recommend": {
"description": "Do not recommend the selected term in any document of this dossier.",
"description-bulk": "Do not recommend the selected terms in any document of this dossier.",
"description": "Do not recommend the selected term in any document of the current dossier.",
"description-bulk": "Do not recommend the selected terms in any document of the current dossier.",
"extraOptionLabel": "Apply to all active and future dossiers",
"label": "Remove from dossier"
},
@ -2057,7 +2054,7 @@
"label": "Remove from dossier in this context"
},
"in-dossier": {
"description": "Do not {type, select, hint{annotate} other{redact}} the selected term in any document of this dossier.",
"description": "Do not {type, select, hint{annotate} other{redact}} the selected term in any document of the current dossier.",
"description-bulk": "Do not auto-redact the selected term in this dossier.",
"extraOptionLabel": "Apply to all active and future dossiers",
"label": "Remove from dossier",

View File

@ -288,14 +288,6 @@
"error": "Fehler beim Speichern der Schwärzung: {error}",
"success": "Schwärzung hinzugefügt!"
},
"change-legal-basis": {
"error": "Fehler beim Bearbeiten der in der Anmerkung genannten Begründung: {error}",
"success": "In der Anmerkung genannte Begründung wurde bearbeitet."
},
"change-type": {
"error": "",
"success": ""
},
"force-hint": {
"error": "Failed to save hint: {error}",
"success": "Hint added!"
@ -304,6 +296,10 @@
"error": "Die Schwärzung konnte nicht gespeichert werden!",
"success": "Schwärzung eingefügt!"
},
"recategorize-annotation": {
"error": "",
"success": ""
},
"recategorize-image": {
"error": "Rekategorisierung des Bildes gescheitert: {error}",
"success": "Bild wurde einer neuen Kategorie zugeordnet."
@ -355,6 +351,7 @@
"annotation-engines": {
"dictionary": "{isHint, select, true{Hint} other{Redaction}} basierend auf Wörterbuch",
"imported": "Annotation is imported",
"manual": "",
"ner": "Redaktion basierend auf KI",
"rule": "Schwärzung basierend auf Regel {rule}"
},

View File

@ -288,14 +288,6 @@
"error": "Failed to save annotation: {error}",
"success": "Annotation added!"
},
"change-legal-basis": {
"error": "Failed to edit annotation reason: {error}",
"success": "Annotation reason was edited."
},
"change-type": {
"error": "",
"success": ""
},
"force-hint": {
"error": "Failed to save hint: {error}",
"success": "Hint added!"
@ -304,6 +296,10 @@
"error": "Failed to save annotation: {error}",
"success": "Annotation added!"
},
"recategorize-annotation": {
"error": "",
"success": ""
},
"recategorize-image": {
"error": "Failed to recategorize image: {error}",
"success": "Image recategorized."
@ -355,6 +351,7 @@
"annotation-engines": {
"dictionary": "{isHint, select, true{Hint} other{Annotation}} based on dictionary",
"imported": "Annotation is imported",
"manual": "Manual",
"ner": "Annotation based on AI",
"rule": "Annotation based on rule {rule}"
},

View File

@ -6,12 +6,11 @@ export type ManualRedactionActions =
| 'add'
| 'remove'
| 'remove-hint'
| 'change-legal-basis'
| 'recategorize-image'
| 'undo'
| 'force-redaction'
| 'force-hint'
| 'change-type';
| 'recategorize-annotation';
export const AnnotationIconTypes = {
square: 'square',

View File

@ -2,4 +2,7 @@ export interface IRecategorizationRequest {
readonly annotationId?: string;
readonly comment?: string;
readonly type?: string;
readonly legalBasis?: string;
readonly section?: string;
readonly value?: string;
}