remove file data model

This commit is contained in:
Dan Percic 2022-03-15 20:20:53 +02:00
parent 38be3c512c
commit 506906694a
5 changed files with 16 additions and 285 deletions

View File

@ -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);
}
}

View File

@ -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([]);
}
}

View File

@ -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,

View File

@ -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);

View File

@ -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) {