From 52ea6fa92d412fb335becc93ac4ceeb9023c7358 Mon Sep 17 00:00:00 2001 From: Valentin Mihai Date: Wed, 13 Dec 2023 12:23:07 +0200 Subject: [PATCH 1/5] RED-8072 - Do not allow any annotations or annotation changes while auto-analysis is disabled --- .../file-preview/services/pdf-annotation-actions.service.ts | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/apps/red-ui/src/app/modules/file-preview/services/pdf-annotation-actions.service.ts b/apps/red-ui/src/app/modules/file-preview/services/pdf-annotation-actions.service.ts index 157a7e963..f967c3f73 100644 --- a/apps/red-ui/src/app/modules/file-preview/services/pdf-annotation-actions.service.ts +++ b/apps/red-ui/src/app/modules/file-preview/services/pdf-annotation-actions.service.ts @@ -73,21 +73,21 @@ export class PdfAnnotationActionsService { availableActions.push(editButton); } - if (permissions.canAcceptRecommendation) { + if (permissions.canAcceptRecommendation && annotationChangesAllowed) { const acceptRecommendationButton = this.#getButton('check', _('annotation-actions.accept-recommendation.label'), () => this.#annotationActionsService.convertRecommendationToAnnotation(annotations), ); availableActions.push(acceptRecommendationButton); } - if (permissions.canForceRedaction) { + if (permissions.canForceRedaction && annotationChangesAllowed) { const forceRedactionButton = this.#getButton('thumb-up', _('annotation-actions.force-redaction.label'), () => this.#annotationActionsService.forceAnnotation(annotations), ); availableActions.push(forceRedactionButton); } - if (permissions.canForceHint) { + if (permissions.canForceHint && annotationChangesAllowed) { const forceHintButton = this.#getButton('thumb-up', _('annotation-actions.force-hint.label'), () => this.#annotationActionsService.forceAnnotation(annotations, true), ); From 023910ce34c9aa94104f38e491839223e5bb2649 Mon Sep 17 00:00:00 2001 From: Nicoleta Panaghiu Date: Wed, 13 Dec 2023 13:54:21 +0200 Subject: [PATCH 2/5] RED-8073: show annotations without type in PDF. --- .../app/models/file/annotation-permissions.utils.ts | 2 +- .../src/app/models/file/annotation.permissions.ts | 2 +- .../src/app/models/file/annotation.wrapper.ts | 8 ++++++-- .../resize-annotation-dialog.component.ts | 4 ++-- .../file-preview/services/file-data.service.ts | 13 +++++++++++-- .../components/type-filter/type-filter.component.ts | 12 ++++++------ 6 files changed, 27 insertions(+), 14 deletions(-) diff --git a/apps/red-ui/src/app/models/file/annotation-permissions.utils.ts b/apps/red-ui/src/app/models/file/annotation-permissions.utils.ts index 6fecfd9a0..a00e57441 100644 --- a/apps/red-ui/src/app/models/file/annotation-permissions.utils.ts +++ b/apps/red-ui/src/app/models/file/annotation-permissions.utils.ts @@ -13,7 +13,7 @@ export const canForceRedaction = (annotation: AnnotationWrapper, canAddRedaction export const canAcceptRecommendation = (annotation: AnnotationWrapper) => annotation.isRecommendation && !annotation.pending; export const canMarkAsFalsePositive = (annotation: AnnotationWrapper, annotationEntity: Dictionary) => - annotation.canBeMarkedAsFalsePositive && annotationEntity.hasDictionary; + annotation.canBeMarkedAsFalsePositive && annotationEntity?.hasDictionary; export const canRemoveOnlyHere = (annotation: AnnotationWrapper, canAddRedaction: boolean) => canAddRedaction && !annotation.pending && (annotation.isRedacted || (annotation.isHint && !annotation.isImage)); diff --git a/apps/red-ui/src/app/models/file/annotation.permissions.ts b/apps/red-ui/src/app/models/file/annotation.permissions.ts index 4abb9d7bb..e99e52326 100644 --- a/apps/red-ui/src/app/models/file/annotation.permissions.ts +++ b/apps/red-ui/src/app/models/file/annotation.permissions.ts @@ -62,7 +62,7 @@ export class AnnotationPermissions { permissions.canRemoveRedaction = canRemoveRedaction(annotation, permissions); permissions.canChangeLegalBasis = canChangeLegalBasis(annotation, canAddRedaction); permissions.canRecategorizeAnnotation = canRecategorizeAnnotation(annotation, canAddRedaction); - permissions.canResizeAnnotation = canResizeAnnotation(annotation, canAddRedaction, annotationEntity.hasDictionary); + permissions.canResizeAnnotation = canResizeAnnotation(annotation, canAddRedaction, annotationEntity?.hasDictionary); permissions.canEditAnnotations = canEditAnnotation(annotation); permissions.canEditHints = canEditHint(annotation); permissions.canEditImages = canEditImage(annotation); diff --git a/apps/red-ui/src/app/models/file/annotation.wrapper.ts b/apps/red-ui/src/app/models/file/annotation.wrapper.ts index 13755fe4c..441b0ae03 100644 --- a/apps/red-ui/src/app/models/file/annotation.wrapper.ts +++ b/apps/red-ui/src/app/models/file/annotation.wrapper.ts @@ -1,9 +1,11 @@ import { marker as _ } from '@biesbjerg/ngx-translate-extract-marker'; import { IListable } from '@iqser/common-ui'; import { + annotationDefaultColorConfig, annotationEntityColorConfig, AnnotationIconType, ChangeType, + DefaultColors, Dictionary, Earmark, EntityTypes, @@ -213,6 +215,7 @@ export class AnnotationWrapper implements IListable { changeLogType: ChangeType, legalBasisList: ILegalBasis[], isDocumine: boolean, + defaultColors: DefaultColors, ) { const annotationWrapper = new AnnotationWrapper(); @@ -273,10 +276,11 @@ export class AnnotationWrapper implements IListable { annotationWrapper.isRemoved = logEntry.state === EntryStates.REMOVED; annotationWrapper.isRemovedLocally = lastRelevantManualChange?.manualRedactionType === ManualRedactionTypes.REMOVE_LOCALLY; - annotationWrapper.typeLabel = dictionary.virtual ? undefined : dictionary.label; + annotationWrapper.typeLabel = dictionary?.virtual ? undefined : dictionary?.label; const colorKey = annotationEntityColorConfig[annotationWrapper.superType]; - annotationWrapper.color = dictionary[colorKey] as string; + const defaultColor = annotationDefaultColorConfig[annotationWrapper.superType]; + annotationWrapper.color = dictionary ? (dictionary[colorKey] as string) : (defaultColors[defaultColor] as string); annotationWrapper['entry'] = logEntry; return annotationWrapper; diff --git a/apps/red-ui/src/app/modules/file-preview/dialogs/docu-mine/resize-annotation-dialog/resize-annotation-dialog.component.ts b/apps/red-ui/src/app/modules/file-preview/dialogs/docu-mine/resize-annotation-dialog/resize-annotation-dialog.component.ts index fb6d55108..b8b6e015f 100644 --- a/apps/red-ui/src/app/modules/file-preview/dialogs/docu-mine/resize-annotation-dialog/resize-annotation-dialog.component.ts +++ b/apps/red-ui/src/app/modules/file-preview/dialogs/docu-mine/resize-annotation-dialog/resize-annotation-dialog.component.ts @@ -26,8 +26,8 @@ export class ResizeAnnotationDialogComponent extends IqserDialogComponent< this.close({ comment: formValue.comment, - updateDictionary: this.entity.hasDictionary, - addToAllDossiers: this.entity.hasDictionary, + updateDictionary: this.entity?.hasDictionary, + addToAllDossiers: this.entity?.hasDictionary, }); } } diff --git a/apps/red-ui/src/app/modules/file-preview/services/file-data.service.ts b/apps/red-ui/src/app/modules/file-preview/services/file-data.service.ts index 35433f183..84505a2f0 100644 --- a/apps/red-ui/src/app/modules/file-preview/services/file-data.service.ts +++ b/apps/red-ui/src/app/modules/file-preview/services/file-data.service.ts @@ -18,6 +18,7 @@ import { firstValueFrom, Observable } from 'rxjs'; import { FilePreviewStateService } from './file-preview-state.service'; import { MultiSelectService } from './multi-select.service'; import { ViewModeService } from './view-mode.service'; +import { DefaultColorsService } from '@services/entity-services/default-colors.service'; const DELTA_VIEW_TIME = 10 * 60 * 1000; // 10 minutes; @@ -55,6 +56,7 @@ export class FileDataService extends EntitiesService Date: Thu, 14 Dec 2023 11:46:25 +0200 Subject: [PATCH 3/5] RED-7691: prevented double error toaster. --- .../services/manual-redaction.service.ts | 39 ++++++++++++------- 1 file changed, 24 insertions(+), 15 deletions(-) diff --git a/apps/red-ui/src/app/modules/file-preview/services/manual-redaction.service.ts b/apps/red-ui/src/app/modules/file-preview/services/manual-redaction.service.ts index 85891f87e..a8fa7fb05 100644 --- a/apps/red-ui/src/app/modules/file-preview/services/manual-redaction.service.ts +++ b/apps/red-ui/src/app/modules/file-preview/services/manual-redaction.service.ts @@ -18,8 +18,8 @@ import type { import { dictionaryActionsTranslations, manualRedactionActionsTranslations } from '@translations/annotation-actions-translations'; import { Roles } from '@users/roles'; import { NGXLogger } from 'ngx-logger'; -import { of } from 'rxjs'; -import { tap } from 'rxjs/operators'; +import { EMPTY, of, OperatorFunction } from 'rxjs'; +import { catchError, tap } from 'rxjs/operators'; function getResponseType(error: boolean, isConflict: boolean) { const isConflictError = isConflict ? 'conflictError' : 'error'; @@ -63,11 +63,13 @@ export class ManualRedactionService extends GenericService { } changeLegalBasis(body: List, dossierId: string, fileId: string) { - return this.legalBasisChange(body, dossierId, fileId).pipe(this.#showToast('change-legal-basis')); + return this.legalBasisChange(body, dossierId, fileId).pipe( + ...(this.#showToast('change-legal-basis') as [OperatorFunction]), + ); } recategorizeRedactions(body: List, dossierId: string, fileId: string) { - return this.recategorize(body, dossierId, fileId).pipe(this.#showToast('change-type')); + return this.recategorize(body, dossierId, fileId).pipe(...(this.#showToast('change-type') as [OperatorFunction])); } addAnnotation( @@ -77,26 +79,32 @@ export class ManualRedactionService extends GenericService { options?: { hint?: boolean; dictionaryLabel?: string }, ) { const toast = requests[0].addToDictionary - ? this.#showAddToDictionaryToast(requests, options?.dictionaryLabel) + ? [this.#showAddToDictionaryToast(requests, options?.dictionaryLabel)] : this.#showToast(options?.hint ? 'force-hint' : 'add'); const canAddRedaction = this._iqserPermissionsService.has(Roles.redactions.write); if (canAddRedaction) { - return this.add(requests, dossierId, fileId).pipe(toast); + return this.add(requests, dossierId, fileId).pipe(...(toast as [OperatorFunction])); } return of(undefined); } bulkForce(requests: List, dossierId: string, fileId: string, isIgnoredHint = false) { - return this.forceRedaction(requests, dossierId, fileId).pipe(this.#showToast(!isIgnoredHint ? 'force-redaction' : 'force-hint')); + return this.forceRedaction(requests, dossierId, fileId).pipe( + ...(this.#showToast(!isIgnoredHint ? 'force-redaction' : 'force-hint') as [OperatorFunction]), + ); } undoRequest(annotationIds: List, dossierId: string, fileId: string, modifyDictionary = false) { - return this.undo(annotationIds, dossierId, fileId).pipe(this.#showToast('undo', modifyDictionary)); + return this.undo(annotationIds, dossierId, fileId).pipe( + ...(this.#showToast('undo', modifyDictionary) as [OperatorFunction]), + ); } removeRedaction(body: List, dossierId: string, fileId: string, removeFromDictionary = false, isHint = false) { - return this.remove(body, dossierId, fileId).pipe(this.#showToast(!isHint ? 'remove' : 'remove-hint', removeFromDictionary)); + return this.remove(body, dossierId, fileId).pipe( + ...(this.#showToast(!isHint ? 'remove' : 'remove-hint', removeFromDictionary) as [OperatorFunction]), + ); } getTitle(type: ManualRedactionEntryType) { @@ -149,17 +157,18 @@ export class ManualRedactionService extends GenericService { }); } - #showToast(action: ManualRedactionActions | DictionaryActions, isDictionary = false) { - return tap({ - next: () => this._toaster.success(getMessage(action, isDictionary), { positionClass: 'toast-file-preview' }), - error: (error: unknown) => { + #showToast(action: ManualRedactionActions | DictionaryActions, isDictionary = false): OperatorFunction[] { + return [ + catchError((error: unknown) => { const isConflict = (error as HttpErrorResponse).status === HttpStatusCode.Conflict; this._toaster.error(getMessage(action, isDictionary, true, isConflict), { error: error as HttpErrorResponse, positionClass: 'toast-file-preview', }); - }, - }); + return EMPTY; + }), + tap(() => this._toaster.success(getMessage(action, isDictionary), { positionClass: 'toast-file-preview' })), + ]; } #showAddToDictionaryToast(body: List, dictionaryLabel?: string) { From 4138f1b1e31db48884931763dc93383f1487c21f Mon Sep 17 00:00:00 2001 From: Nicoleta Panaghiu Date: Thu, 14 Dec 2023 12:33:48 +0200 Subject: [PATCH 4/5] RED-7691: refactor code. --- .../services/manual-redaction.service.ts | 43 ++++++++----------- 1 file changed, 19 insertions(+), 24 deletions(-) diff --git a/apps/red-ui/src/app/modules/file-preview/services/manual-redaction.service.ts b/apps/red-ui/src/app/modules/file-preview/services/manual-redaction.service.ts index a8fa7fb05..184d986bd 100644 --- a/apps/red-ui/src/app/modules/file-preview/services/manual-redaction.service.ts +++ b/apps/red-ui/src/app/modules/file-preview/services/manual-redaction.service.ts @@ -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 } from 'rxjs'; +import { EMPTY, of, OperatorFunction, pipe } from 'rxjs'; import { catchError, tap } from 'rxjs/operators'; function getResponseType(error: boolean, isConflict: boolean) { @@ -63,13 +63,11 @@ export class ManualRedactionService extends GenericService { } changeLegalBasis(body: List, dossierId: string, fileId: string) { - return this.legalBasisChange(body, dossierId, fileId).pipe( - ...(this.#showToast('change-legal-basis') as [OperatorFunction]), - ); + return this.legalBasisChange(body, dossierId, fileId).pipe(this.#showToast('change-legal-basis')); } recategorizeRedactions(body: List, dossierId: string, fileId: string) { - return this.recategorize(body, dossierId, fileId).pipe(...(this.#showToast('change-type') as [OperatorFunction])); + return this.recategorize(body, dossierId, fileId).pipe(this.#showToast('change-type')); } addAnnotation( @@ -79,32 +77,26 @@ export class ManualRedactionService extends GenericService { options?: { hint?: boolean; dictionaryLabel?: string }, ) { const toast = requests[0].addToDictionary - ? [this.#showAddToDictionaryToast(requests, options?.dictionaryLabel)] + ? this.#showAddToDictionaryToast(requests, options?.dictionaryLabel) : this.#showToast(options?.hint ? 'force-hint' : 'add'); const canAddRedaction = this._iqserPermissionsService.has(Roles.redactions.write); if (canAddRedaction) { - return this.add(requests, dossierId, fileId).pipe(...(toast as [OperatorFunction])); + return this.add(requests, dossierId, fileId).pipe(toast); } return of(undefined); } bulkForce(requests: List, dossierId: string, fileId: string, isIgnoredHint = false) { - return this.forceRedaction(requests, dossierId, fileId).pipe( - ...(this.#showToast(!isIgnoredHint ? 'force-redaction' : 'force-hint') as [OperatorFunction]), - ); + return this.forceRedaction(requests, dossierId, fileId).pipe(this.#showToast(!isIgnoredHint ? 'force-redaction' : 'force-hint')); } undoRequest(annotationIds: List, dossierId: string, fileId: string, modifyDictionary = false) { - return this.undo(annotationIds, dossierId, fileId).pipe( - ...(this.#showToast('undo', modifyDictionary) as [OperatorFunction]), - ); + return this.undo(annotationIds, dossierId, fileId).pipe(this.#showToast('undo', modifyDictionary)); } removeRedaction(body: List, dossierId: string, fileId: string, removeFromDictionary = false, isHint = false) { - return this.remove(body, dossierId, fileId).pipe( - ...(this.#showToast(!isHint ? 'remove' : 'remove-hint', removeFromDictionary) as [OperatorFunction]), - ); + return this.remove(body, dossierId, fileId).pipe(this.#showToast(!isHint ? 'remove' : 'remove-hint', removeFromDictionary)); } getTitle(type: ManualRedactionEntryType) { @@ -157,8 +149,8 @@ export class ManualRedactionService extends GenericService { }); } - #showToast(action: ManualRedactionActions | DictionaryActions, isDictionary = false): OperatorFunction[] { - return [ + #showToast(action: ManualRedactionActions | DictionaryActions, isDictionary = false) { + return pipe( catchError((error: unknown) => { const isConflict = (error as HttpErrorResponse).status === HttpStatusCode.Conflict; this._toaster.error(getMessage(action, isDictionary, true, isConflict), { @@ -168,13 +160,12 @@ export class ManualRedactionService extends GenericService { return EMPTY; }), tap(() => this._toaster.success(getMessage(action, isDictionary), { positionClass: 'toast-file-preview' })), - ]; + ); } #showAddToDictionaryToast(body: List, dictionaryLabel?: string) { - return tap({ - next: () => this._toaster.success(getDictionaryMessage('add'), { positionClass: 'toast-file-preview' }), - error: (error: unknown) => { + return pipe( + catchError((error: unknown) => { const isConflict = (error as HttpErrorResponse).status === HttpStatusCode.Conflict; this._toaster.error(getDictionaryMessage('add', true, isConflict), { error: error as HttpErrorResponse, @@ -184,7 +175,11 @@ export class ManualRedactionService extends GenericService { }, positionClass: 'toast-file-preview', }); - }, - }); + return EMPTY; + }), + tap({ + next: () => this._toaster.success(getDictionaryMessage('add'), { positionClass: 'toast-file-preview' }), + }), + ); } } From ed349f06972e618c9ebe3f5b425d004a174fc846 Mon Sep 17 00:00:00 2001 From: Nicoleta Panaghiu Date: Thu, 14 Dec 2023 12:43:06 +0200 Subject: [PATCH 5/5] RED-7691: refactor code. --- .../modules/file-preview/services/manual-redaction.service.ts | 4 +--- 1 file changed, 1 insertion(+), 3 deletions(-) diff --git a/apps/red-ui/src/app/modules/file-preview/services/manual-redaction.service.ts b/apps/red-ui/src/app/modules/file-preview/services/manual-redaction.service.ts index 184d986bd..2864bdc5f 100644 --- a/apps/red-ui/src/app/modules/file-preview/services/manual-redaction.service.ts +++ b/apps/red-ui/src/app/modules/file-preview/services/manual-redaction.service.ts @@ -177,9 +177,7 @@ export class ManualRedactionService extends GenericService { }); return EMPTY; }), - tap({ - next: () => this._toaster.success(getDictionaryMessage('add'), { positionClass: 'toast-file-preview' }), - }), + tap(() => this._toaster.success(getDictionaryMessage('add'), { positionClass: 'toast-file-preview' })), ); } }