RED-3747: add rectangles to multiple pages

This commit is contained in:
Dan Percic 2022-04-08 00:32:03 +03:00
parent 7d4424cf24
commit 4fa6ae4c08
6 changed files with 123 additions and 45 deletions

View File

@ -3,39 +3,35 @@
<div [translate]="title" class="dialog-header heading-l"></div>
<div class="dialog-content">
<ng-container *ngIf="!data.manualRedactionEntryWrapper.manualRedactionEntry.rectangle">
<div class="iqser-input-group w-400">
<label translate="manual-annotation.dialog.content.text"></label>
<div class="flex-align-items-center" *ngIf="!isEditingSelectedText">
{{ form.get('selectedText').value }}
<iqser-circle-button
*ngIf="isDictionaryRequest"
(action)="isEditingSelectedText = true"
[tooltip]="'manual-annotation.dialog.content.edit-selected-text' | translate"
[type]="circleButtonTypes.dark"
icon="iqser:edit"
tooltipPosition="below"
></iqser-circle-button>
</div>
<textarea
*ngIf="isEditingSelectedText"
formControlName="selectedText"
iqserHasScrollbar
name="comment"
rows="4"
type="text"
>
</textarea>
<div *ngIf="!isRectangle" class="iqser-input-group w-450">
<label translate="manual-annotation.dialog.content.text"></label>
<div *ngIf="!isEditingSelectedText" class="flex-align-items-center">
{{ form.get('selectedText').value }}
<iqser-circle-button
(action)="isEditingSelectedText = true"
*ngIf="isDictionaryRequest"
[tooltip]="'manual-annotation.dialog.content.edit-selected-text' | translate"
[type]="circleButtonTypes.dark"
icon="iqser:edit"
tooltipPosition="below"
></iqser-circle-button>
</div>
</ng-container>
<textarea
*ngIf="isEditingSelectedText"
formControlName="selectedText"
iqserHasScrollbar
name="comment"
rows="4"
type="text"
>
</textarea>
</div>
<ng-container *ngIf="data.manualRedactionEntryWrapper.manualRedactionEntry.rectangle">
<div class="iqser-input-group">
<label translate="manual-annotation.dialog.content.rectangle"></label>
</div>
</ng-container>
<div *ngIf="isRectangle" class="iqser-input-group">
<label translate="manual-annotation.dialog.content.rectangle"></label>
</div>
<div *ngIf="!isFalsePositiveRequest && isDictionaryRequest" class="iqser-input-group required w-400">
<div *ngIf="!isFalsePositiveRequest && isDictionaryRequest" class="iqser-input-group required w-450">
<label translate="manual-annotation.dialog.content.dictionary"></label>
<mat-select formControlName="dictionary">
@ -53,7 +49,7 @@
</mat-select>
</div>
<div *ngIf="!isDictionaryRequest" class="iqser-input-group required w-400">
<div *ngIf="!isDictionaryRequest" class="iqser-input-group required w-450">
<label translate="manual-annotation.dialog.content.reason"></label>
<mat-select
[placeholder]="'manual-annotation.dialog.content.reason-placeholder' | translate"
@ -71,25 +67,41 @@
</mat-select>
</div>
<div *ngIf="!isDictionaryRequest" class="iqser-input-group w-400">
<div *ngIf="!isDictionaryRequest" class="iqser-input-group w-450">
<label translate="manual-annotation.dialog.content.legalBasis"></label>
<input [value]="form.get('reason').value?.legalBasis" disabled type="text" />
</div>
<div *ngIf="data.manualRedactionEntryWrapper.manualRedactionEntry.rectangle" class="iqser-input-group w-400">
<div *ngIf="isRectangle" class="iqser-input-group w-450">
<label translate="manual-annotation.dialog.content.section"></label>
<input formControlName="section" name="section" type="text" />
</div>
<div *ngIf="data.manualRedactionEntryWrapper.manualRedactionEntry.rectangle" class="iqser-input-group w-400">
<div *ngIf="isRectangle" class="iqser-input-group w-450">
<label translate="manual-annotation.dialog.content.classification"></label>
<input formControlName="classification" name="classification" type="text" />
</div>
<div class="iqser-input-group w-300">
<div class="iqser-input-group w-450">
<label translate="manual-annotation.dialog.content.comment"></label>
<textarea formControlName="comment" iqserHasScrollbar name="comment" rows="4" type="text"></textarea>
</div>
<div *ngIf="isRectangle" class="apply-on-multiple-pages iqser-input-group w-450">
<mat-checkbox #checkbox class="mb-15" color="primary">
{{ 'manual-annotation.dialog.content.apply-on-multiple-pages' | translate }}
</mat-checkbox>
<div *ngIf="checkbox.checked">
<input
[placeholder]="'manual-annotation.dialog.content.apply-on-multiple-pages-placeholder' | translate"
class="full-width"
formControlName="multiplePages"
/>
<span class="hint">{{ 'manual-annotation.dialog.content.apply-on-multiple-pages-hint' | translate }}</span>
</div>
</div>
</div>
<div class="dialog-actions">

View File

@ -1,3 +1,23 @@
.full-width {
width: 100%;
}
.apply-on-multiple-pages {
min-height: 55px;
display: flex;
flex-direction: row;
align-items: center;
&.iqser-input-group input {
margin-top: 0;
}
mat-checkbox {
width: fit-content;
margin-right: 16px;
}
}
.mb-15 {
margin-bottom: 15px;
}

View File

@ -6,9 +6,10 @@ import { JustificationsService } from '@services/entity-services/justifications.
import { Dictionary, Dossier, IAddRedactionRequest } from '@red/domain';
import { ActiveDossiersService } from '@services/dossiers/active-dossiers.service';
import { DictionaryService } from '@services/entity-services/dictionary.service';
import { BaseDialogComponent, CircleButtonTypes } from '@iqser/common-ui';
import { BaseDialogComponent, CircleButtonTypes, Toaster } from '@iqser/common-ui';
import { firstValueFrom } from 'rxjs';
import { ManualRedactionService } from '../../services/manual-redaction.service';
import { marker as _ } from '@biesbjerg/ngx-translate-extract-marker';
export interface LegalBasisOption {
label?: string;
@ -38,8 +39,9 @@ export class ManualAnnotationDialogComponent extends BaseDialogComponent impleme
private readonly _activeDossiersService: ActiveDossiersService,
private readonly _dictionaryService: DictionaryService,
protected readonly _injector: Injector,
protected readonly _toaster: Toaster,
protected readonly _dialogRef: MatDialogRef<ManualAnnotationDialogComponent>,
@Inject(MAT_DIALOG_DATA) public data: { manualRedactionEntryWrapper: ManualRedactionEntryWrapper; dossierId: string },
@Inject(MAT_DIALOG_DATA) readonly data: { manualRedactionEntryWrapper: ManualRedactionEntryWrapper; dossierId: string },
) {
super(_injector, _dialogRef);
this._dossier = this._activeDossiersService.find(this.data.dossierId);
@ -55,6 +57,10 @@ export class ManualAnnotationDialogComponent extends BaseDialogComponent impleme
return this._manualRedactionService.getTitle(this.data.manualRedactionEntryWrapper.type, this._dossier);
}
get isRectangle() {
return !!this.data.manualRedactionEntryWrapper.manualRedactionEntry.rectangle;
}
get displayedDictionaryLabel() {
const dictType = this.form.get('dictionary').value;
if (dictType) {
@ -81,14 +87,43 @@ export class ManualAnnotationDialogComponent extends BaseDialogComponent impleme
}));
this.legalOptions.sort((a, b) => a.label.localeCompare(b.label));
if (!this.data.manualRedactionEntryWrapper.manualRedactionEntry.rectangle) {
if (!this.isRectangle) {
this._formatSelectedTextValue();
}
}
save() {
this._enhanceManualRedaction(this.data.manualRedactionEntryWrapper.manualRedactionEntry);
this._dialogRef.close(this.data.manualRedactionEntryWrapper);
try {
const annotations = this.isRectangle ? this.#getRectangles() : [this.data.manualRedactionEntryWrapper];
this._dialogRef.close(annotations);
} catch (e) {
this._toaster.error(_('manual-annotation.dialog.error'));
}
}
#getRectangles() {
const quads = this.data.manualRedactionEntryWrapper.manualRedactionEntry.positions.find(a => !!a);
const value: string = this.form.get('multiplePages').value.replace(/[^0-9-,]/g, '');
const entry = { ...this.data.manualRedactionEntryWrapper.manualRedactionEntry };
const wrapper = { ...this.data.manualRedactionEntryWrapper };
const wrappers: ManualRedactionEntryWrapper[] = [wrapper];
value.split(',').forEach(range => {
const splitted = range.split('-');
const startPage = parseInt(splitted[0], 10);
const endPage = splitted.length > 1 ? parseInt(splitted[1], 10) : startPage;
if (!startPage || !endPage) {
throw new Error();
}
for (let page = startPage; page <= endPage; page++) {
const manualRedactionEntry = { ...entry, positions: [{ ...quads, page }] };
wrappers.push({ ...wrapper, manualRedactionEntry });
}
});
return wrappers;
}
private _formatSelectedTextValue() {
@ -110,6 +145,7 @@ export class ManualAnnotationDialogComponent extends BaseDialogComponent impleme
: ['manual', Validators.required],
comment: [null],
classification: ['non-readable content'],
multiplePages: '',
});
}

View File

@ -30,7 +30,7 @@ import { FileWorkloadComponent } from './components/file-workload/file-workload.
import { TranslateService } from '@ngx-translate/core';
import { FilesService } from '@services/entity-services/files.service';
import { FileManagementService } from '@services/entity-services/file-management.service';
import { catchError, debounceTime, map, startWith, switchMap, tap, withLatestFrom } from 'rxjs/operators';
import { catchError, debounceTime, map, startWith, switchMap, tap } from 'rxjs/operators';
import { FilesMapService } from '@services/entity-services/files-map.service';
import { WatermarkService } from '@shared/services/watermark.service';
import { ExcludedPagesService } from './services/excluded-pages.service';
@ -266,12 +266,14 @@ export class FilePreviewScreenComponent extends AutoUnsubscribe implements OnIni
'manualAnnotation',
null,
{ manualRedactionEntryWrapper, dossierId: this.dossierId },
({ manualRedactionEntry }: ManualRedactionEntryWrapper) => {
const add$ = this._manualRedactionService.addAnnotation([manualRedactionEntry], this.dossierId, this.fileId);
const addAndReload$ = add$.pipe(
withLatestFrom(this.state.file$),
switchMap(([, file]) => this._filesService.reload(this.dossierId, file)),
async (wrappers: ManualRedactionEntryWrapper[]) => {
const file = await this.state.file;
const add$ = this._manualRedactionService.addAnnotation(
wrappers.map(w => w.manualRedactionEntry).filter(e => e.positions[0].page <= file.numberOfPages),
this.dossierId,
this.fileId,
);
const addAndReload$ = add$.pipe(switchMap(() => this._filesService.reload(this.dossierId, file)));
return firstValueFrom(addAndReload$.pipe(catchError(() => of(undefined))));
},
);

View File

@ -1516,6 +1516,9 @@
"save": "Speichern"
},
"content": {
"apply-on-multiple-pages": "",
"apply-on-multiple-pages-hint": "",
"apply-on-multiple-pages-placeholder": "",
"classification": "Wert / Klassifizierung",
"comment": "Kommentar",
"dictionary": "Wörterbuch",
@ -1527,6 +1530,7 @@
"section": "Absatz / Ort",
"text": "Ausgewählter Text:"
},
"error": "",
"header": {
"dictionary": "Zum Wörterbuch hinzufügen",
"false-positive": "Als Falsch-Positiv definieren",

View File

@ -1516,6 +1516,9 @@
"save": "Save"
},
"content": {
"apply-on-multiple-pages": "Apply on multiple pages",
"apply-on-multiple-pages-hint": "Minus(-) for range and comma(,) for enumeration.",
"apply-on-multiple-pages-placeholder": "e.g. 1-20,22,32",
"classification": "Value / Classification",
"comment": "Comment",
"dictionary": "Dictionary",
@ -1527,6 +1530,7 @@
"section": "Paragraph / Location",
"text": "Selected text:"
},
"error": "Error! Invalid page selection",
"header": {
"dictionary": "Add to dictionary",
"false-positive": "Set false positive",