remove file data model
This commit is contained in:
parent
38be3c512c
commit
506906694a
@ -1,208 +0,0 @@
|
||||
import {
|
||||
ChangeType,
|
||||
Dictionary,
|
||||
File,
|
||||
IRedactionLog,
|
||||
IRedactionLogEntry,
|
||||
IViewedPage,
|
||||
LogEntryStatus,
|
||||
ManualRedactionType,
|
||||
TextHighlightResponse,
|
||||
ViewMode,
|
||||
} from '@red/domain';
|
||||
import { AnnotationWrapper } from './annotation.wrapper';
|
||||
import * as moment from 'moment';
|
||||
import { BehaviorSubject } from 'rxjs';
|
||||
import { RedactionLogEntry } from './redaction-log.entry';
|
||||
|
||||
export class FileDataModel {
|
||||
static readonly DELTA_VIEW_TIME = 10 * 60 * 1000; // 10 minutes;
|
||||
allAnnotations: AnnotationWrapper[] = [];
|
||||
readonly hasChangeLog$ = new BehaviorSubject<boolean>(false);
|
||||
missingTypes = new Set<string>();
|
||||
textHighlightAnnotations: AnnotationWrapper[] = [];
|
||||
|
||||
constructor(
|
||||
private readonly _file: File,
|
||||
private _redactionLog: IRedactionLog,
|
||||
public viewedPages?: IViewedPage[],
|
||||
private _dictionaryData?: Dictionary[],
|
||||
private _areDevFeaturesEnabled?: boolean,
|
||||
) {
|
||||
this._buildAllAnnotations();
|
||||
}
|
||||
|
||||
get redactionLog(): IRedactionLog {
|
||||
return this._redactionLog;
|
||||
}
|
||||
|
||||
set redactionLog(redactionLog: IRedactionLog) {
|
||||
this._redactionLog = redactionLog;
|
||||
this._buildAllAnnotations();
|
||||
}
|
||||
|
||||
set textHighlights(textHighlightResponse: TextHighlightResponse) {
|
||||
const highlights = [];
|
||||
// eslint-disable-next-line @typescript-eslint/no-unsafe-argument
|
||||
for (const color of Object.keys(textHighlightResponse.redactionPerColor)) {
|
||||
for (const entry of textHighlightResponse.redactionPerColor[color]) {
|
||||
const annotation = AnnotationWrapper.fromHighlight(color, entry);
|
||||
highlights.push(annotation);
|
||||
}
|
||||
}
|
||||
this.textHighlightAnnotations = highlights;
|
||||
}
|
||||
|
||||
getVisibleAnnotations(viewMode: ViewMode) {
|
||||
if (viewMode === 'TEXT_HIGHLIGHTS') {
|
||||
return this.textHighlightAnnotations;
|
||||
}
|
||||
|
||||
return this.allAnnotations.filter(annotation => {
|
||||
if (viewMode === 'STANDARD') {
|
||||
return !annotation.isChangeLogRemoved;
|
||||
} else if (viewMode === 'DELTA') {
|
||||
return annotation.isChangeLogEntry;
|
||||
} else {
|
||||
return annotation.previewAnnotation;
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
private _buildAllAnnotations() {
|
||||
const entries: RedactionLogEntry[] = this._convertData();
|
||||
|
||||
const previousAnnotations = [...this.allAnnotations];
|
||||
this.allAnnotations = entries
|
||||
.map(entry => AnnotationWrapper.fromData(entry))
|
||||
.filter(ann => ann.manual || !this._file.excludedPages.includes(ann.pageNumber));
|
||||
|
||||
if (!this._areDevFeaturesEnabled) {
|
||||
this.allAnnotations = this.allAnnotations.filter(annotation => !annotation.isFalsePositive);
|
||||
}
|
||||
|
||||
this._setHiddenPropertyToNewAnnotations(this.allAnnotations, previousAnnotations);
|
||||
}
|
||||
|
||||
private _setHiddenPropertyToNewAnnotations(newAnnotations: AnnotationWrapper[], oldAnnotations: AnnotationWrapper[]) {
|
||||
newAnnotations.forEach(newAnnotation => {
|
||||
const oldAnnotation = oldAnnotations.find(a => a.annotationId === newAnnotation.annotationId);
|
||||
if (oldAnnotation) {
|
||||
newAnnotation.hidden = oldAnnotation.hidden;
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
private _convertData(): RedactionLogEntry[] {
|
||||
let result: RedactionLogEntry[] = [];
|
||||
|
||||
const reasonAnnotationIds: { [key: string]: RedactionLogEntry[] } = {};
|
||||
this.redactionLog.redactionLogEntry?.forEach(redactionLogEntry => {
|
||||
// copy the redactionLog Entry
|
||||
|
||||
const changeLogValues = this.#getChangeLogValues(redactionLogEntry);
|
||||
const dictionaryData = this._dictionaryData.find(dict => dict.type === redactionLogEntry.type);
|
||||
if (!dictionaryData) {
|
||||
this.missingTypes.add(redactionLogEntry.type);
|
||||
return;
|
||||
}
|
||||
|
||||
const redactionLogEntryWrapper: RedactionLogEntry = new RedactionLogEntry(
|
||||
redactionLogEntry,
|
||||
changeLogValues.changeLogType,
|
||||
changeLogValues.isChangeLogEntry,
|
||||
changeLogValues.hidden,
|
||||
this.redactionLog.legalBasis,
|
||||
!!dictionaryData?.hint,
|
||||
);
|
||||
|
||||
if (
|
||||
redactionLogEntry.manualChanges?.find(
|
||||
mc =>
|
||||
mc.manualRedactionType === ManualRedactionType.ADD_TO_DICTIONARY &&
|
||||
(mc.annotationStatus === LogEntryStatus.APPROVED || mc.annotationStatus === LogEntryStatus.REQUESTED),
|
||||
)
|
||||
) {
|
||||
// for dictionary entries -> I.E accepted recommendations or false positives,
|
||||
// check reason
|
||||
if (!reasonAnnotationIds[redactionLogEntry.reason]) {
|
||||
reasonAnnotationIds[redactionLogEntry.reason] = [redactionLogEntryWrapper];
|
||||
} else {
|
||||
reasonAnnotationIds[redactionLogEntry.reason].push(redactionLogEntryWrapper);
|
||||
}
|
||||
}
|
||||
|
||||
result.push(redactionLogEntryWrapper);
|
||||
});
|
||||
|
||||
const reasonKeys = Object.keys(reasonAnnotationIds);
|
||||
result = result.filter(r => {
|
||||
const matched = reasonKeys.indexOf(r.id) >= 0;
|
||||
if (matched) {
|
||||
reasonAnnotationIds[r.id].forEach(value => {
|
||||
value.reason = null;
|
||||
});
|
||||
}
|
||||
return !matched;
|
||||
});
|
||||
|
||||
result = result.filter(r => !r.hidden);
|
||||
|
||||
return result;
|
||||
}
|
||||
|
||||
#getChangeLogValues(redactionLogEntry: IRedactionLogEntry): {
|
||||
hidden: boolean;
|
||||
changeLogType: ChangeType;
|
||||
isChangeLogEntry: boolean;
|
||||
} {
|
||||
if (this._file.numberOfAnalyses > 1) {
|
||||
const viableChanges = redactionLogEntry.changes.filter(c => c.analysisNumber > 1);
|
||||
viableChanges.sort((a, b) => moment(a.dateTime).valueOf() - moment(b.dateTime).valueOf());
|
||||
|
||||
const lastChange = viableChanges.length >= 1 ? viableChanges[viableChanges.length - 1] : undefined;
|
||||
const page = redactionLogEntry.positions?.[0].page;
|
||||
|
||||
const viewedPage = this.viewedPages.filter(p => p.page === page).pop();
|
||||
|
||||
// page has been seen -> let's see if it's a change
|
||||
if (viewedPage) {
|
||||
const viewTime = moment(viewedPage.viewedTime).valueOf() - FileDataModel.DELTA_VIEW_TIME;
|
||||
// these are all unseen changes
|
||||
const relevantChanges = viableChanges.filter(change => moment(change.dateTime).valueOf() > viewTime);
|
||||
// at least one unseen change
|
||||
if (relevantChanges.length > 0) {
|
||||
// at least 1 relevant change
|
||||
viewedPage.showAsUnseen = moment(viewedPage.viewedTime).valueOf() < moment(lastChange.dateTime).valueOf();
|
||||
this.hasChangeLog$.next(true);
|
||||
return {
|
||||
changeLogType: relevantChanges[relevantChanges.length - 1].type,
|
||||
isChangeLogEntry: true,
|
||||
hidden: false,
|
||||
};
|
||||
} else {
|
||||
// no relevant changes - hide removed anyway
|
||||
return {
|
||||
changeLogType: null,
|
||||
isChangeLogEntry: false,
|
||||
hidden: lastChange && lastChange.type === 'REMOVED',
|
||||
};
|
||||
}
|
||||
} else {
|
||||
// Page doesn't have a view-time
|
||||
return {
|
||||
changeLogType: null,
|
||||
isChangeLogEntry: false,
|
||||
hidden: lastChange && lastChange.type === 'REMOVED',
|
||||
};
|
||||
}
|
||||
} else {
|
||||
return {
|
||||
changeLogType: null,
|
||||
isChangeLogEntry: false,
|
||||
hidden: false,
|
||||
};
|
||||
}
|
||||
// console.log(wrapper.changeLogType, wrapper.hidden, wrapper.isChangeLogEntry, wrapper.value, lastChange);
|
||||
}
|
||||
}
|
||||
@ -1,65 +0,0 @@
|
||||
import { Injectable } from '@angular/core';
|
||||
import { forkJoin, Observable, of } from 'rxjs';
|
||||
import { catchError, map, tap } from 'rxjs/operators';
|
||||
import { FileDataModel } from '@models/file/file-data.model';
|
||||
import { PermissionsService } from '@services/permissions.service';
|
||||
import { Dictionary, File, IRedactionLog, IViewedPage, TextHighlightResponse } from '@red/domain';
|
||||
import { RedactionLogService } from './redaction-log.service';
|
||||
import { ViewedPagesService } from '@services/entity-services/viewed-pages.service';
|
||||
import { UserPreferenceService } from '@services/user-preference.service';
|
||||
import { FilePreviewStateService } from '../../file-preview/services/file-preview-state.service';
|
||||
import { Toaster } from '@iqser/common-ui';
|
||||
import { marker as _ } from '@biesbjerg/ngx-translate-extract-marker';
|
||||
import { DictionariesMapService } from '@services/entity-services/dictionaries-map.service';
|
||||
import { TextHighlightService } from './text-highlight.service';
|
||||
|
||||
@Injectable()
|
||||
export class PdfViewerDataService {
|
||||
constructor(
|
||||
private readonly _permissionsService: PermissionsService,
|
||||
private readonly _redactionLogService: RedactionLogService,
|
||||
private readonly _textHighlightService: TextHighlightService,
|
||||
private readonly _viewedPagesService: ViewedPagesService,
|
||||
private readonly _userPreferenceService: UserPreferenceService,
|
||||
private readonly _stateService: FilePreviewStateService,
|
||||
private readonly _toaster: Toaster,
|
||||
private readonly _dictionariesMapService: DictionariesMapService,
|
||||
) {}
|
||||
|
||||
loadRedactionLogFor(dossierId: string, fileId: string) {
|
||||
return this._redactionLogService.getRedactionLog(dossierId, fileId).pipe(
|
||||
tap(redactionLog => redactionLog.redactionLogEntry.sort((a, b) => a.positions[0].page - b.positions[0].page)),
|
||||
catchError(() => of({})),
|
||||
);
|
||||
}
|
||||
|
||||
loadTextHighlightsFor(dossierId: string, fileId: string): Observable<TextHighlightResponse> {
|
||||
return this._textHighlightService.getTextHighlights(dossierId, fileId).pipe(catchError(() => of({})));
|
||||
}
|
||||
|
||||
loadDataFor(newFile: File): Observable<FileDataModel> {
|
||||
const redactionLog$ = this.loadRedactionLogFor(newFile.dossierId, newFile.fileId);
|
||||
const viewedPages$ = this.getViewedPagesFor(newFile);
|
||||
|
||||
return forkJoin([redactionLog$, viewedPages$]).pipe(
|
||||
map((data: [redactionLog: IRedactionLog, viewedPages: IViewedPage[]]) => {
|
||||
const dictionaries: Dictionary[] = this._dictionariesMapService.get(this._stateService.dossierTemplateId);
|
||||
const fileDataModel = new FileDataModel(newFile, ...data, dictionaries, this._userPreferenceService.areDevFeaturesEnabled);
|
||||
if (fileDataModel.missingTypes.size > 0) {
|
||||
this._toaster.error(_('error.missing-types'), {
|
||||
disableTimeOut: true,
|
||||
params: { missingTypes: Array.from(fileDataModel.missingTypes).join(', ') },
|
||||
});
|
||||
}
|
||||
return fileDataModel;
|
||||
}),
|
||||
);
|
||||
}
|
||||
|
||||
getViewedPagesFor(file: File) {
|
||||
if (this._permissionsService.canMarkPagesAsViewed(file)) {
|
||||
return this._viewedPagesService.getViewedPages(file.dossierId, file.fileId).pipe(catchError(() => of([])));
|
||||
}
|
||||
return of([]);
|
||||
}
|
||||
}
|
||||
@ -7,7 +7,6 @@ import { SkippedService } from './services/skipped.service';
|
||||
import { AnnotationDrawService } from './services/annotation-draw.service';
|
||||
import { AnnotationActionsService } from './services/annotation-actions.service';
|
||||
import { FilePreviewStateService } from './services/file-preview-state.service';
|
||||
import { PdfViewerDataService } from '../dossier/services/pdf-viewer-data.service';
|
||||
import { AnnotationReferencesService } from './services/annotation-references.service';
|
||||
import { FilterService } from '@iqser/common-ui';
|
||||
import { AnnotationProcessingService } from '../dossier/services/annotation-processing.service';
|
||||
@ -27,7 +26,6 @@ export const filePreviewScreenProviders = [
|
||||
AnnotationDrawService,
|
||||
AnnotationActionsService,
|
||||
FilePreviewStateService,
|
||||
PdfViewerDataService,
|
||||
AnnotationReferencesService,
|
||||
PageRotationService,
|
||||
PdfViewer,
|
||||
|
||||
@ -25,7 +25,6 @@ import { File, ViewMode } from '@red/domain';
|
||||
import { PermissionsService } from '@services/permissions.service';
|
||||
import { combineLatest, firstValueFrom, Observable, of, timer } from 'rxjs';
|
||||
import { UserPreferenceService } from '@services/user-preference.service';
|
||||
import { PdfViewerDataService } from '../dossier/services/pdf-viewer-data.service';
|
||||
import { clearStamps, download, handleFilterDelta, stampPDFPage } from '../../utils';
|
||||
import { FileWorkloadComponent } from './components/file-workload/file-workload.component';
|
||||
import { TranslateService } from '@ngx-translate/core';
|
||||
@ -93,7 +92,6 @@ export class FilePreviewScreenComponent extends AutoUnsubscribe implements OnIni
|
||||
private readonly _router: Router,
|
||||
private readonly _annotationProcessingService: AnnotationProcessingService,
|
||||
private readonly _annotationDrawService: AnnotationDrawService,
|
||||
private readonly _pdfViewerDataService: PdfViewerDataService,
|
||||
private readonly _filesService: FilesService,
|
||||
private readonly _ngZone: NgZone,
|
||||
private readonly _fileManagementService: FileManagementService,
|
||||
@ -196,9 +194,8 @@ export class FilePreviewScreenComponent extends AutoUnsubscribe implements OnIni
|
||||
}
|
||||
case 'TEXT_HIGHLIGHTS': {
|
||||
this._loadingService.start();
|
||||
const textHighlights = await firstValueFrom(this._pdfViewerDataService.loadTextHighlightsFor(this.dossierId, this.fileId));
|
||||
this._pdf.hideAnnotations(annotations);
|
||||
this._fileDataService.textHighlights = textHighlights;
|
||||
await this._fileDataService.loadTextHighlights();
|
||||
await this._annotationDrawService.drawAnnotations(this._fileDataService.textHighlightAnnotations);
|
||||
this._loadingService.stop();
|
||||
}
|
||||
@ -613,9 +610,7 @@ export class FilePreviewScreenComponent extends AutoUnsubscribe implements OnIni
|
||||
}
|
||||
|
||||
const currentPageAnnotations = this.visibleAnnotations.filter(a => a.pageNumber === page);
|
||||
await this._fileDataService.setRedactionLog(
|
||||
await firstValueFrom(this._pdfViewerDataService.loadRedactionLogFor(this.dossierId, this.fileId)),
|
||||
);
|
||||
await this._fileDataService.loadRedactionLog();
|
||||
|
||||
this._deleteAnnotations(currentPageAnnotations);
|
||||
await this._cleanupAndRedrawAnnotations(annotation => annotation.pageNumber === page);
|
||||
|
||||
@ -23,6 +23,7 @@ import { PermissionsService } from '../../../services/permissions.service';
|
||||
import { marker as _ } from '@biesbjerg/ngx-translate-extract-marker';
|
||||
import { Toaster } from '../../../../../../../libs/common-ui/src';
|
||||
import { RedactionLogService } from '../../dossier/services/redaction-log.service';
|
||||
import { TextHighlightService } from '../../dossier/services/text-highlight.service';
|
||||
|
||||
@Injectable()
|
||||
export class FileDataService {
|
||||
@ -43,6 +44,7 @@ export class FileDataService {
|
||||
private readonly _dictionariesMapService: DictionariesMapService,
|
||||
private readonly _permissionsService: PermissionsService,
|
||||
private readonly _redactionLogService: RedactionLogService,
|
||||
private readonly _textHighlightsService: TextHighlightService,
|
||||
private readonly _toaster: Toaster,
|
||||
) {}
|
||||
|
||||
@ -70,12 +72,13 @@ export class FileDataService {
|
||||
|
||||
this.shouldUpdateAnnotations = true;
|
||||
this.#redactionLogHash = newRedactionLogHash;
|
||||
this.missingTypes.clear();
|
||||
await this.#buildAllAnnotations();
|
||||
}
|
||||
|
||||
async load(file: File) {
|
||||
this.viewedPages = await firstValueFrom(this.getViewedPagesFor(file));
|
||||
await this.setRedactionLog(await firstValueFrom(this.loadRedactionLog()));
|
||||
await this.loadRedactionLog();
|
||||
|
||||
if (this.missingTypes.size > 0) {
|
||||
this._toaster.error(_('error.missing-types'), {
|
||||
@ -85,6 +88,12 @@ export class FileDataService {
|
||||
}
|
||||
}
|
||||
|
||||
async loadTextHighlights() {
|
||||
const { dossierId, fileId } = this._state;
|
||||
const highlights = this._textHighlightsService.getTextHighlights(dossierId, fileId).pipe(catchError(() => of({})));
|
||||
this.textHighlights = await firstValueFrom(highlights);
|
||||
}
|
||||
|
||||
getViewedPagesFor(file: File) {
|
||||
if (this._permissionsService.canMarkPagesAsViewed(file)) {
|
||||
return this._viewedPagesService.getViewedPages(file.dossierId, file.fileId);
|
||||
@ -92,11 +101,13 @@ export class FileDataService {
|
||||
return of([] as IViewedPage[]);
|
||||
}
|
||||
|
||||
loadRedactionLog() {
|
||||
return this._redactionLogService.getRedactionLog(this._state.dossierId, this._state.fileId).pipe(
|
||||
async loadRedactionLog() {
|
||||
const redactionLog$ = this._redactionLogService.getRedactionLog(this._state.dossierId, this._state.fileId).pipe(
|
||||
tap(redactionLog => redactionLog.redactionLogEntry.sort((a, b) => a.positions[0].page - b.positions[0].page)),
|
||||
catchError(() => of({})),
|
||||
);
|
||||
|
||||
return this.setRedactionLog(await firstValueFrom(redactionLog$));
|
||||
}
|
||||
|
||||
getVisibleAnnotations(viewMode: ViewMode) {
|
||||
|
||||
Loading…
x
Reference in New Issue
Block a user