RED-3721: New highlights endpoints
This commit is contained in:
parent
6a7e438c63
commit
3bb3e93906
@ -1,6 +1,6 @@
|
||||
import { annotationTypesTranslations } from '../../translations/annotation-types-translations';
|
||||
import { marker as _ } from '@biesbjerg/ngx-translate-extract-marker';
|
||||
import { IComment, IManualChange, ImportedRedaction, IPoint, IRectangle, LogEntryStatus, ManualRedactionType } from '@red/domain';
|
||||
import { Highlight, IComment, IManualChange, IPoint, IRectangle, LogEntryStatus, ManualRedactionType } from '@red/domain';
|
||||
import { RedactionLogEntry } from '@models/file/redaction-log.entry';
|
||||
import {
|
||||
FalsePositiveSuperTypes,
|
||||
@ -228,17 +228,17 @@ export class AnnotationWrapper implements IListable, Record<string, unknown> {
|
||||
);
|
||||
}
|
||||
|
||||
static fromHighlight(color: string, entry: ImportedRedaction) {
|
||||
static fromHighlight(highlight: Highlight) {
|
||||
const annotationWrapper = new AnnotationWrapper();
|
||||
|
||||
annotationWrapper.annotationId = entry.id;
|
||||
annotationWrapper.pageNumber = entry.positions[0].page;
|
||||
annotationWrapper.annotationId = highlight.id;
|
||||
annotationWrapper.pageNumber = highlight.positions[0].page;
|
||||
annotationWrapper.superType = SuperTypes.TextHighlight;
|
||||
annotationWrapper.typeValue = SuperTypes.TextHighlight;
|
||||
annotationWrapper.value = 'Imported';
|
||||
annotationWrapper.color = color;
|
||||
annotationWrapper.positions = entry.positions;
|
||||
annotationWrapper.firstTopLeftPoint = entry.positions[0]?.topLeft;
|
||||
annotationWrapper.color = highlight.hexColor;
|
||||
annotationWrapper.positions = highlight.positions;
|
||||
annotationWrapper.firstTopLeftPoint = highlight.positions[0]?.topLeft;
|
||||
annotationWrapper.typeLabel = annotationTypesTranslations[annotationWrapper.superType];
|
||||
|
||||
return annotationWrapper;
|
||||
|
||||
@ -1,40 +1,41 @@
|
||||
import { Injectable, Injector } from '@angular/core';
|
||||
import { GenericService, RequiredParam, Toaster, Validate } from '@iqser/common-ui';
|
||||
import { ImportedRedaction, TextHighlightOperation, TextHighlightRequest, TextHighlightResponse } from '@red/domain';
|
||||
import { marker as _ } from '@biesbjerg/ngx-translate-extract-marker';
|
||||
import { Highlight, TextHighlightOperation, TextHighlightResponse } from '@red/domain';
|
||||
import { catchError, map, tap } from 'rxjs/operators';
|
||||
import { of } from 'rxjs';
|
||||
import { Observable, of } from 'rxjs';
|
||||
import { AnnotationWrapper } from '@models/file/annotation.wrapper';
|
||||
import { marker as _ } from '@biesbjerg/ngx-translate-extract-marker';
|
||||
|
||||
@Injectable({
|
||||
providedIn: 'root',
|
||||
})
|
||||
export class TextHighlightService extends GenericService<TextHighlightResponse> {
|
||||
constructor(protected readonly _injector: Injector, private readonly _toaster: Toaster) {
|
||||
super(_injector, 'texthighlights-conversion');
|
||||
super(_injector, '');
|
||||
}
|
||||
|
||||
@Validate()
|
||||
getTextHighlights(@RequiredParam() dossierId: string, @RequiredParam() fileId: string) {
|
||||
const request: TextHighlightRequest = {
|
||||
dossierId,
|
||||
fileId,
|
||||
operation: TextHighlightOperation.INFO,
|
||||
};
|
||||
|
||||
return this._post(request).pipe(
|
||||
map(response => response.redactionPerColor),
|
||||
catchError(() => of({} as Record<string, ImportedRedaction[]>)),
|
||||
getTextHighlights(@RequiredParam() dossierId: string, @RequiredParam() fileId: string): Observable<AnnotationWrapper[]> {
|
||||
return this._http.get<{ highlights: Highlight[] }>(`/${this.#getPath(dossierId, fileId)}`).pipe(
|
||||
map(response => response.highlights),
|
||||
map(highlights => highlights.map(highlight => AnnotationWrapper.fromHighlight(highlight))),
|
||||
catchError(() => of([])),
|
||||
);
|
||||
}
|
||||
|
||||
@Validate()
|
||||
performHighlightsAction(
|
||||
@RequiredParam() ids: string[],
|
||||
@RequiredParam() dossierId: string,
|
||||
@RequiredParam() fileId: string,
|
||||
@RequiredParam() colors: string[],
|
||||
@RequiredParam() operation: TextHighlightOperation,
|
||||
) {
|
||||
const request: TextHighlightRequest = { dossierId, fileId, colors, operation };
|
||||
return this._post(request).pipe(tap(() => this._toaster.success(_('highlight-action-dialog.success'), { params: { operation } })));
|
||||
return this._post({ ids }, `${this.#getPath(dossierId, fileId)}/${operation}`).pipe(
|
||||
tap(() => this._toaster.success(_('highlight-action-dialog.success'), { params: { operation } })),
|
||||
);
|
||||
}
|
||||
|
||||
#getPath(dossierId: string, fileId: string): string {
|
||||
return `dossiers/${dossierId}/files/${fileId}/highlights`;
|
||||
}
|
||||
}
|
||||
|
||||
@ -4,6 +4,7 @@ import { TextHighlightOperation, TextHighlightsGroup } from '@red/domain';
|
||||
import { FilePreviewStateService } from '../../services/file-preview-state.service';
|
||||
import { AnnotationWrapper } from '@models/file/annotation.wrapper';
|
||||
import { FilePreviewDialogService } from '../../services/file-preview-dialog.service';
|
||||
import { FileDataService } from '../../services/file-data.service';
|
||||
|
||||
@Component({
|
||||
selector: 'redaction-highlights-separator [highlightGroup] [annotation]',
|
||||
@ -18,7 +19,11 @@ export class HighlightsSeparatorComponent {
|
||||
readonly circleButtonTypes = CircleButtonTypes;
|
||||
readonly isWritable$ = this._state.isWritable$;
|
||||
|
||||
constructor(private readonly _dialogService: FilePreviewDialogService, private readonly _state: FilePreviewStateService) {}
|
||||
constructor(
|
||||
private readonly _dialogService: FilePreviewDialogService,
|
||||
private readonly _state: FilePreviewStateService,
|
||||
private readonly _fileDataService: FileDataService,
|
||||
) {}
|
||||
|
||||
convertHighlights(highlightGroup: TextHighlightsGroup): void {
|
||||
const data = this._getActionData(highlightGroup, TextHighlightOperation.CONVERT);
|
||||
@ -31,11 +36,13 @@ export class HighlightsSeparatorComponent {
|
||||
}
|
||||
|
||||
private _getActionData(highlightGroup: TextHighlightsGroup, operation: TextHighlightOperation) {
|
||||
const highlights = this._fileDataService.all.filter(a => a.color === highlightGroup.color);
|
||||
return {
|
||||
dossierId: this._state.dossierId,
|
||||
fileId: this._state.fileId,
|
||||
color: highlightGroup.color,
|
||||
operation,
|
||||
highlights,
|
||||
};
|
||||
}
|
||||
}
|
||||
|
||||
@ -6,12 +6,14 @@ import { BaseDialogComponent, LoadingService } from '@iqser/common-ui';
|
||||
import { TextHighlightService } from '../../../dossier/services/text-highlight.service';
|
||||
import { firstValueFrom } from 'rxjs';
|
||||
import { marker as _ } from '@biesbjerg/ngx-translate-extract-marker';
|
||||
import { AnnotationWrapper } from '@models/file/annotation.wrapper';
|
||||
|
||||
export interface HighlightActionData {
|
||||
readonly operation: TextHighlightOperation;
|
||||
readonly color: string;
|
||||
readonly dossierId: string;
|
||||
readonly fileId: string;
|
||||
readonly highlights: AnnotationWrapper[];
|
||||
}
|
||||
|
||||
@Component({
|
||||
@ -55,8 +57,9 @@ export class HighlightActionDialogComponent extends BaseDialogComponent {
|
||||
|
||||
async save(): Promise<void> {
|
||||
this._loadingService.start();
|
||||
const { dossierId, fileId, color, operation } = this.data;
|
||||
await firstValueFrom(this._textHighlightService.performHighlightsAction(dossierId, fileId, [color], operation));
|
||||
const { dossierId, fileId, operation, highlights } = this.data;
|
||||
const ids = highlights.map(h => h.id);
|
||||
await firstValueFrom(this._textHighlightService.performHighlightsAction(ids, dossierId, fileId, operation));
|
||||
this._loadingService.stop();
|
||||
this._dialogRef.close(true);
|
||||
}
|
||||
|
||||
@ -1,7 +1,6 @@
|
||||
import {
|
||||
ChangeType,
|
||||
File,
|
||||
ImportedRedaction,
|
||||
IRedactionLog,
|
||||
IRedactionLogEntry,
|
||||
IViewedPage,
|
||||
@ -100,12 +99,11 @@ export class FileDataService extends EntitiesService<AnnotationWrapper> {
|
||||
await this.loadRedactionLog();
|
||||
}
|
||||
|
||||
async loadTextHighlights() {
|
||||
const redactionPerColor = this._textHighlightsService.getTextHighlights(this._state.dossierId, this._state.fileId);
|
||||
const textHighlights = this.#buildTextHighlights(await firstValueFrom(redactionPerColor));
|
||||
this.#textHighlights$.next(textHighlights);
|
||||
async loadTextHighlights(): Promise<AnnotationWrapper[]> {
|
||||
const highlights = await firstValueFrom(this._textHighlightsService.getTextHighlights(this._state.dossierId, this._state.fileId));
|
||||
this.#textHighlights$.next(highlights);
|
||||
|
||||
return textHighlights;
|
||||
return highlights;
|
||||
}
|
||||
|
||||
loadRedactionLog() {
|
||||
@ -159,15 +157,6 @@ export class FileDataService extends EntitiesService<AnnotationWrapper> {
|
||||
});
|
||||
}
|
||||
|
||||
#buildTextHighlights(redactionPerColor: Record<string, ImportedRedaction[]>): AnnotationWrapper[] {
|
||||
if (!redactionPerColor) {
|
||||
return [];
|
||||
}
|
||||
|
||||
const highlights = Object.entries(redactionPerColor);
|
||||
return highlights.flatMap(([color, entries]) => entries.map(entry => AnnotationWrapper.fromHighlight(color, entry)));
|
||||
}
|
||||
|
||||
#buildAnnotations(redactionLog: IRedactionLog, file: File) {
|
||||
const entries: RedactionLogEntry[] = this.#convertData(redactionLog, file);
|
||||
const annotations = entries.map(entry => AnnotationWrapper.fromData(entry));
|
||||
|
||||
@ -1436,7 +1436,7 @@
|
||||
"save": "Remove Highlights",
|
||||
"title": "Remove highlights"
|
||||
},
|
||||
"success": "{operation, select, CONVERT{Converting} REMOVE{Removing} other{}} highlights in progress..."
|
||||
"success": "{operation, select, convert{Converting} delete{Removing} other{}} highlights in progress..."
|
||||
},
|
||||
"highlights": "{color} - {length} {length, plural, one{highlight} other{highlights}}",
|
||||
"image-category": {
|
||||
|
||||
7
libs/red-domain/src/lib/text-highlight/highlight.ts
Normal file
7
libs/red-domain/src/lib/text-highlight/highlight.ts
Normal file
@ -0,0 +1,7 @@
|
||||
import { IRectangle } from '../geometry';
|
||||
|
||||
export interface Highlight {
|
||||
readonly id: string;
|
||||
readonly positions: IRectangle[];
|
||||
readonly hexColor: string;
|
||||
}
|
||||
@ -1,6 +0,0 @@
|
||||
import { IRectangle } from '../geometry';
|
||||
|
||||
export interface ImportedRedaction {
|
||||
id: string;
|
||||
positions: IRectangle[];
|
||||
}
|
||||
@ -1,4 +1,4 @@
|
||||
export * from './imported-redaction';
|
||||
export * from './highlight';
|
||||
export * from './text-highlight-operation';
|
||||
export * from './text-highlight.response';
|
||||
export * from './text-highlight.request';
|
||||
|
||||
@ -1,5 +1,4 @@
|
||||
export enum TextHighlightOperation {
|
||||
REMOVE = 'REMOVE',
|
||||
CONVERT = 'CONVERT',
|
||||
INFO = 'INFO',
|
||||
REMOVE = 'delete',
|
||||
CONVERT = 'convert',
|
||||
}
|
||||
|
||||
@ -1,9 +1,9 @@
|
||||
import { ImportedRedaction } from './imported-redaction';
|
||||
import { Highlight } from './highlight';
|
||||
import { TextHighlightOperation } from './text-highlight-operation';
|
||||
|
||||
export interface TextHighlightResponse {
|
||||
dossierId?: string;
|
||||
fileId?: string;
|
||||
operation?: TextHighlightOperation;
|
||||
redactionPerColor?: Record<string, ImportedRedaction[]>;
|
||||
redactionPerColor?: Record<string, Highlight[]>;
|
||||
}
|
||||
|
||||
Loading…
x
Reference in New Issue
Block a user