RED-6774 - updated dialog logic to be able to redact and add hints

This commit is contained in:
Valentin Mihai 2023-06-19 07:45:26 +03:00
parent 8f26b3bffc
commit 2d76b6c1eb
16 changed files with 137 additions and 67 deletions

View File

@ -79,7 +79,7 @@ export class AnnotationActionsComponent implements OnChanges {
}
removeOrSuggestRemoveRedaction() {
this.annotationActionsService.removeOrSuggestRemoveRedaction(this.annotations);
this.annotationActionsService.removeOrSuggestRemoveRedaction(this.annotations[0]);
}
markAsFalsePositive() {

View File

@ -72,10 +72,10 @@
<div class="dialog-actions">
<iqser-icon-button
[disabled]="disabled"
[label]="'redact-text.dialog.actions.save' | translate"
[submit]="true"
[type]="iconButtonTypes.primary"
[disabled]="disabled"
>
</iqser-icon-button>

View File

@ -1,7 +1,7 @@
import { Component, Inject, OnInit } from '@angular/core';
import { BaseDialogComponent, DetailsRadioOption, IqserPermissionsService } from '@iqser/common-ui';
import { MAT_DIALOG_DATA, MatDialogRef } from '@angular/material/dialog';
import { Dictionary, Dossier, File, IAddRedactionRequest } from '@red/domain';
import { Dictionary, Dossier, File, IAddRedactionRequest, SuperTypes } from '@red/domain';
import { UntypedFormGroup, Validators } from '@angular/forms';
import { Roles } from '@users/roles';
import { firstValueFrom } from 'rxjs';
@ -10,7 +10,11 @@ import { ActiveDossiersService } from '@services/dossiers/active-dossiers.servic
import { LegalBasisOption } from '../manual-redaction-dialog/manual-annotation-dialog.component';
import { DictionaryService } from '@services/entity-services/dictionary.service';
import { marker as _ } from '@biesbjerg/ngx-translate-extract-marker';
import { ManualRedactionEntryType, ManualRedactionEntryWrapper } from '@models/file/manual-redaction-entry.wrapper';
import {
ManualRedactionEntryType,
ManualRedactionEntryTypes,
ManualRedactionEntryWrapper,
} from '@models/file/manual-redaction-entry.wrapper';
import { redactTextTranslations } from '@translations/redact-text-translations';
import { RedactTextOption, RedactTextOptions } from './redact-text-options';
import { tap } from 'rxjs/operators';
@ -30,8 +34,11 @@ export class RedactTextDialogComponent extends BaseDialogComponent implements On
legalOptions: LegalBasisOption[] = [];
dictionaries: Dictionary[] = [];
#manualRedactionTypeExists = true;
readonly #translations = redactTextTranslations;
readonly #dossier: Dossier;
readonly #hint: boolean;
constructor(
private _justificationsService: JustificationsService,
@ -45,14 +52,31 @@ export class RedactTextDialogComponent extends BaseDialogComponent implements On
this.#dossier = _activeDossiersService.find(this.data.dossierId);
this.type = this.data.manualRedactionEntryWrapper.type;
this.#hint = this.type === ManualRedactionEntryTypes.HINT;
this.#manualRedactionTypeExists = this._dictionaryService.hasManualType(this.#dossier.dossierTemplateId);
this.options = this.#options();
this.form = this.#getForm();
this.initialFormValue = this.form.getRawValue();
}
get displayedDictionaryLabel() {
const dictType = this.form.get('dictionary').value;
if (dictType) {
return this.dictionaries.find(d => d.type === dictType)?.label ?? null;
}
return null;
}
get disabled() {
if (this.dictionaryRequest || this.#hint) {
return !this.form.get('dictionary').value;
}
return !this.form.get('reason').value;
}
async ngOnInit(): Promise<void> {
this.dictionaries = await this._dictionaryService.getDictionariesOptions(this.#dossier.dossierTemplateId, this.#dossier.id);
this.dictionaries = this._dictionaryService.getPossibleDictionaries(this.#dossier.dossierTemplateId, this.#hint);
const data = await firstValueFrom(this._justificationsService.getForDossierTemplate(this.#dossier.dossierTemplateId));
this.legalOptions = data.map(lbm => ({
@ -60,6 +84,7 @@ export class RedactTextDialogComponent extends BaseDialogComponent implements On
description: lbm.description,
label: lbm.name,
}));
this.legalOptions.sort((a, b) => a.label.localeCompare(b.label));
this.#selectReason();
this.#formatSelectedTextValue();
@ -70,31 +95,20 @@ export class RedactTextDialogComponent extends BaseDialogComponent implements On
.valueChanges.pipe(
tap((option: DetailsRadioOption<RedactTextOption>) => {
this.dictionaryRequest = option.value === RedactTextOptions.IN_DOSSIER;
this.#resetValues();
}),
)
.subscribe(),
);
}
get displayedDictionaryLabel() {
const dictType = this.form.get('dictionary').value;
if (dictType) {
return this.dictionaries.find(d => d.type === dictType).label;
}
return null;
}
save(): void {
this.#enhanceManualRedaction(this.data.manualRedactionEntryWrapper.manualRedactionEntry);
try {
const annotation = this.data.manualRedactionEntryWrapper.manualRedactionEntry;
this._dialogRef.close({
annotation,
dictionary: this.dictionaries.find(d => d.type === this.form.get('dictionary').value),
});
} catch (e) {
this._toaster.error(_('manual-annotation.dialog.error'));
}
const redaction = this.data.manualRedactionEntryWrapper.manualRedactionEntry;
this._dialogRef.close({
redaction,
dictionary: this.dictionaries.find(d => d.type === this.form.get('dictionary').value),
});
}
#getForm(): UntypedFormGroup {
@ -102,9 +116,9 @@ export class RedactTextDialogComponent extends BaseDialogComponent implements On
selectedText: this.data?.manualRedactionEntryWrapper?.manualRedactionEntry?.value,
reason: [null],
comment: [null],
dictionary: [null, Validators.required],
classification: '',
multiplePages: '',
dictionary: [this.#manualRedactionTypeExists ? SuperTypes.ManualRedaction : null],
classification: ['non-readable content'],
section: [null],
option: [this.options[0]],
});
}
@ -162,9 +176,23 @@ export class RedactTextDialogComponent extends BaseDialogComponent implements On
{
label: this.#translations[this.type].inDossier.label,
description: this.#translations[this.type].inDossier.description,
descriptionParams: { dossierName: this.#dossier.dossierName },
icon: FOLDER_ICON,
value: RedactTextOptions.IN_DOSSIER,
// extraOption: {
// label: this.#translations[this.type].inDossier.extraOptionLabel,
// checked: true,
// },
},
];
}
#resetValues() {
if (this.dictionaryRequest) {
this.form.get('reason').setValue(null);
this.form.get('dictionary').setValue(null);
return;
}
this.form.get('dictionary').setValue(this.#manualRedactionTypeExists ? SuperTypes.ManualRedaction : null);
}
}

View File

@ -4,26 +4,37 @@ import { MAT_DIALOG_DATA, MatDialogRef } from '@angular/material/dialog';
import { RemoveRedactionOption, RemoveRedactionOptions } from './remove-redaction-options';
import { UntypedFormGroup } from '@angular/forms';
import { removeRedactionTranslations } from '@translations/remove-redaction-translations';
import { AnnotationWrapper } from '@models/file/annotation.wrapper';
import { Dossier } from '@red/domain';
const PIN_ICON = 'red:push-pin';
const FOLDER_ICON = 'red:folder';
const REMOVE_FROM_DICT_ICON = 'red:remove-from-dict';
export interface RemoveRedactionData {
redaction: AnnotationWrapper;
dossier: Dossier;
hint: boolean;
}
@Component({
templateUrl: './remove-redaction-dialog.html',
styleUrls: ['./remove-redaction-dialog.component.scss'],
})
export class RemoveRedactionDialogComponent extends BaseDialogComponent {
readonly options: DetailsRadioOption<RemoveRedactionOption>[];
readonly #redaction: AnnotationWrapper;
readonly #translations = removeRedactionTranslations;
constructor(
protected readonly _dialogRef: MatDialogRef<RemoveRedactionDialogComponent>,
@Inject(MAT_DIALOG_DATA) readonly data: NonNullable<unknown>,
@Inject(MAT_DIALOG_DATA) readonly data: RemoveRedactionData,
) {
super(_dialogRef);
this.#redaction = data.redaction;
console.log('redaction: ', this.#redaction);
this.options = this.#options();
this.form = this.#getForm();
this.initialFormValue = this.form.getRawValue();
@ -45,18 +56,21 @@ export class RemoveRedactionDialogComponent extends BaseDialogComponent {
{
label: this.#translations.ONLY_HERE.label,
description: this.#translations.ONLY_HERE.description,
descriptionParams: { value: this.#redaction.value },
icon: PIN_ICON,
value: RemoveRedactionOptions.ONLY_HERE,
},
{
label: this.#translations.IN_DOSSIER.label,
description: this.#translations.IN_DOSSIER.description,
descriptionParams: { value: this.#redaction.value },
icon: FOLDER_ICON,
value: RemoveRedactionOptions.IN_DOSSIER,
},
{
label: this.#translations.FALSE_POSITIVE.label,
description: this.#translations.FALSE_POSITIVE.description,
descriptionParams: { value: this.#redaction.value, type: this.#redaction.type },
icon: REMOVE_FROM_DICT_ICON,
value: RemoveRedactionOptions.FALSE_POSITIVE,
},

View File

@ -163,15 +163,15 @@ export class FilePreviewScreenComponent
});
effect(() => {
// const selectedText = this._documentViewer.selectedText();
// const canPerformActions = this.pdfProxyService.canPerformActions();
// const isCurrentPageExcluded = this.state.file().isPageExcluded(this.pdf.currentPage());
//
// if ((selectedText.length > 2 || this._isJapaneseString(selectedText)) && canPerformActions && !isCurrentPageExcluded) {
// this.pdf.enable(textActions);
// } else {
// this.pdf.disable(textActions);
// }
const selectedText = this._documentViewer.selectedText();
const canPerformActions = this.pdfProxyService.canPerformActions();
const isCurrentPageExcluded = this.state.file().isPageExcluded(this.pdf.currentPage());
if ((selectedText.length > 2 || this._isJapaneseString(selectedText)) && canPerformActions && !isCurrentPageExcluded) {
this.pdf.enable(textActions);
} else {
this.pdf.disable(textActions);
}
});
effect(() => {
@ -370,18 +370,12 @@ export class FilePreviewScreenComponent
return this._ngZone.run(() => {
const file = this.state.file();
manualRedactionEntryWrapper.manualRedactionEntry.value = 'This is selected text';
this._dialogService.openDialog(
'redactText',
{ manualRedactionEntryWrapper, dossierId: this.dossierId, file },
(result: { annotation: IManualRedactionEntry; dictionary?: Dictionary }) => {
const selectedAnnotations = this._annotationManager.selected;
if (selectedAnnotations.length > 0) {
this._annotationManager.delete([selectedAnnotations[0].Id]);
}
(result: { redaction: IManualRedactionEntry; dictionary?: Dictionary }) => {
const add$ = this._manualRedactionService.addAnnotation(
[result.annotation],
[result.redaction],
this.dossierId,
this.fileId,
result.dictionary?.label,

View File

@ -30,6 +30,7 @@ import { PdfViewer } from '../../pdf-viewer/services/pdf-viewer.service';
import { REDAnnotationManager } from '../../pdf-viewer/services/annotation-manager.service';
import { SkippedService } from './skipped.service';
import { REDDocumentViewer } from '../../pdf-viewer/services/document-viewer.service';
import { RemoveRedactionData } from '../dialogs/remove-redaction-dialog/remove-redaction-dialog.component';
@Injectable()
export class AnnotationActionsService {
@ -129,20 +130,20 @@ export class AnnotationActionsService {
});
}
removeOrSuggestRemoveRedaction(annotations: AnnotationWrapper[]) {
const data = {
annotationsToRemove: annotations,
removeOrSuggestRemoveRedaction(redaction: AnnotationWrapper) {
const data: RemoveRedactionData = {
redaction,
dossier: this._state.dossier(),
hint: annotations[0].hintDictionary,
hint: redaction.hintDictionary,
};
const { dossierId, fileId } = this._state;
this._dialogService.openDialog('removeRedaction', data, (result: { comment: string }) => {
const body = annotations.map(annotation => ({
annotationId: annotation.id,
const body = {
annotationId: redaction.id,
comment: result.comment,
}));
};
const { dossierId, fileId } = this._state;
this.#processObsAndEmit(
this._manualRedactionService.removeOrSuggestRemove(body, dossierId, fileId, true, annotations[0].isHint),
this._manualRedactionService.removeOrSuggestRemove([body], dossierId, fileId, true, redaction.isHint),
).then();
});
}

View File

@ -131,7 +131,7 @@ export class PdfAnnotationActionsService {
if (permissions.canRemoveOrSuggestToRemoveOnlyHere) {
const removeOrSuggestToRemoveButton = this.#getButton('trash', _('annotation-actions.remove-annotation.remove-redaction'), () =>
this.#annotationActionsService.removeOrSuggestRemoveRedaction(annotations),
this.#annotationActionsService.removeOrSuggestRemoveRedaction(annotations[0]),
);
availableActions.push(removeOrSuggestToRemoveButton);
}

View File

@ -230,7 +230,7 @@ export class PdfProxyService {
this._pdf.enable(TEXT_POPUPS_TO_TOGGLE);
this._viewerHeaderService.enable(HEADER_ITEMS_TO_TOGGLE);
if (this._documentViewer.selectedText()?.length > 2) {
if (this._documentViewer.selectedText().length > 2) {
this._pdf.enable([TextPopups.ADD_DICTIONARY, TextPopups.ADD_FALSE_POSITIVE]);
}
} else {

View File

@ -145,6 +145,24 @@ export class DictionaryService extends EntitiesService<IDictionary, Dictionary>
return !!this._dictionariesMapService.get(dossierTemplateId).find(e => e.type === SuperTypes.ManualRedaction && !e.virtual);
}
getPossibleDictionaries(dossierTemplateId: string, hintTypes: boolean): Dictionary[] {
const possibleDictionaries: Dictionary[] = [];
this._dictionariesMapService.get(dossierTemplateId).forEach((d: Dictionary) => {
if (!hintTypes) {
if (!d.virtual && !d.hint && !d.systemManaged) {
possibleDictionaries.push(d);
}
} else if (d.hint) {
possibleDictionaries.push(d);
}
});
possibleDictionaries.sort((a, b) => a.label.localeCompare(b.label));
return possibleDictionaries;
}
getRedactionTypes(dossierTemplateId: string): Dictionary[] {
const possibleDictionaries: Dictionary[] = [];

View File

@ -3,6 +3,7 @@ import { marker as _ } from '@biesbjerg/ngx-translate-extract-marker';
interface Option {
label: string;
description: string;
extraOptionLabel?: string;
}
export const redactTextTranslations: Record<'REDACT' | 'HINT', Record<'onlyHere' | 'inDossier', Option>> = {
@ -14,6 +15,7 @@ export const redactTextTranslations: Record<'REDACT' | 'HINT', Record<'onlyHere'
inDossier: {
label: _('redact-text.dialog.content.options.redact.in-dossier.label'),
description: _('redact-text.dialog.content.options.redact.in-dossier.description'),
extraOptionLabel: _('redact-text.dialog.content.options.redact.in-dossier.extraOptionLabel'),
},
},
HINT: {
@ -24,6 +26,7 @@ export const redactTextTranslations: Record<'REDACT' | 'HINT', Record<'onlyHere'
inDossier: {
label: _('redact-text.dialog.content.options.hint.in-dossier.label'),
description: _('redact-text.dialog.content.options.hint.in-dossier.description'),
extraOptionLabel: _('redact-text.dialog.content.options.hint.in-dossier.extraOptionLabel'),
},
},
} as const;

View File

@ -1,7 +1,7 @@
{
"ADMIN_CONTACT_NAME": null,
"ADMIN_CONTACT_URL": null,
"API_URL": "https://dev-04.iqser.cloud/redaction-gateway-v1",
"API_URL": "https://dev-08.iqser.cloud/redaction-gateway-v1",
"APP_NAME": "RedactManager",
"AUTO_READ_TIME": 3,
"BACKEND_APP_VERSION": "4.4.40",
@ -11,7 +11,7 @@
"MAX_RETRIES_ON_SERVER_ERROR": 3,
"OAUTH_CLIENT_ID": "redaction",
"OAUTH_IDP_HINT": null,
"OAUTH_URL": "https://dev-04.iqser.cloud/auth",
"OAUTH_URL": "https://dev-08.iqser.cloud/auth",
"RECENT_PERIOD_IN_HOURS": 24,
"SELECTION_MODE": "structural",
"MANUAL_BASE_URL": "https://docs.redactmanager.com/preview",

View File

@ -1915,6 +1915,7 @@
"hint": {
"in-dossier": {
"description": "",
"extraOptionLabel": "",
"label": ""
},
"only-here": {
@ -1925,6 +1926,7 @@
"redact": {
"in-dossier": {
"description": "",
"extraOptionLabel": "",
"label": ""
},
"only-here": {
@ -1939,6 +1941,7 @@
"type": "",
"type-placeholder": ""
},
"error": "",
"title": ""
}
},

View File

@ -1914,7 +1914,8 @@
"options": {
"hint": {
"in-dossier": {
"description": "Add hint in every document in Dossier Alpha.",
"description": "Add hint in every document in {dossierName}.",
"extraOptionLabel": "Apply to all dossiers",
"label": "Add hint in dossier"
},
"only-here": {
@ -1924,7 +1925,8 @@
},
"redact": {
"in-dossier": {
"description": "Add redaction in every document in Dossier Alpha.",
"description": "Add redaction in every document in {dossierName}.",
"extraOptionLabel": "Apply to all dossiers",
"label": "Redact in dossier"
},
"only-here": {
@ -1939,6 +1941,7 @@
"type": "Type",
"type-placeholder": "Select type ..."
},
"error": "Error! Invalid page selection",
"title": "Redact text"
}
},
@ -1970,15 +1973,15 @@
"comment-placeholder": "Add remarks or mentions ...",
"options": {
"false-positive": {
"description": "\"White\" is not a CBI Author in this context: \"White rabbits are\".",
"description": "\"{value}\" is not a {type} in this context: \"{context}\".",
"label": "False positive"
},
"in-dossier": {
"description": "Do not redact \"White\" in any document of the current dossier.",
"description": "Do not redact \"{value}\" in any document of the current dossier.",
"label": "Remove in dossier"
},
"only-here": {
"description": "Do not redact \"White\" at this position in the current document.",
"description": "Do not redact \"{value}\" at this position in the current document.",
"label": "Remove here"
}
}

View File

@ -1915,6 +1915,7 @@
"hint": {
"in-dossier": {
"description": "",
"extraOptionLabel": "",
"label": ""
},
"only-here": {
@ -1925,6 +1926,7 @@
"redact": {
"in-dossier": {
"description": "",
"extraOptionLabel": "",
"label": ""
},
"only-here": {
@ -1939,6 +1941,7 @@
"type": "",
"type-placeholder": ""
},
"error": "",
"title": ""
}
},

View File

@ -1914,7 +1914,8 @@
"options": {
"hint": {
"in-dossier": {
"description": "Add hint in every document in Dossier Alpha.",
"description": "Add hint in every document in {dossierName}.",
"extraOptionLabel": "Apply to all dossiers",
"label": "Add hint in dossier"
},
"only-here": {
@ -1924,7 +1925,8 @@
},
"redact": {
"in-dossier": {
"description": "Add redaction in every document in Dossier Alpha.",
"description": "Add redaction in every document in {dossierName}.",
"extraOptionLabel": "Apply to all dossiers",
"label": "Redact in dossier"
},
"only-here": {
@ -1939,6 +1941,7 @@
"type": "Type",
"type-placeholder": "Select type ..."
},
"error": "Error! Invalid page selection",
"title": "Redact text"
}
},
@ -1970,15 +1973,15 @@
"comment-placeholder": "Add remarks or mentions ...",
"options": {
"false-positive": {
"description": "\"White\" is not a CBI Author in this context: \"White rabbits are\".",
"description": "\"{value}\" is not a {type} in this context: \"{context}\".",
"label": "False positive"
},
"in-dossier": {
"description": "Do not redact \"White\" in any document of the current dossier.",
"description": "Do not redact \"{value}\" in any document of the current dossier.",
"label": "Remove in dossier"
},
"only-here": {
"description": "Do not redact \"White\" at this position in the current document.",
"description": "Do not redact \"{value}\" at this position in the current document.",
"label": "Remove here"
}
}

@ -1 +1 @@
Subproject commit 490c01f85b4d97e69549b9ad0f206223b9456f5c
Subproject commit ae296dec490ace06295b3baef3777262f8fdc83c