delta view corrections

This commit is contained in:
Timo Bejan 2022-01-11 16:55:06 +02:00
parent e05bbb017e
commit d7fefef737
14 changed files with 111 additions and 111 deletions

View File

@ -3,34 +3,34 @@ import { AnnotationWrapper } from './annotation.wrapper';
import { RedactionLogEntryWrapper } from './redaction-log-entry.wrapper';
import * as moment from 'moment';
export class AnnotationData {
visibleAnnotations: AnnotationWrapper[];
allAnnotations: AnnotationWrapper[];
}
export class FileDataModel {
static readonly DELTA_VIEW_TIME = 10 * 60 * 1000; // 10 minutes;
hasChangeLog: boolean;
allAnnotations: AnnotationWrapper[];
constructor(public file: File, public fileData: Blob, public redactionLog: IRedactionLog, public viewedPages?: IViewedPage[]) {}
constructor(
public file: File,
public fileData: Blob,
private _redactionLog: IRedactionLog,
public viewedPages?: IViewedPage[],
private _dictionaryData?: { [p: string]: Dictionary },
private _areDevFeaturesEnabled?: boolean,
) {
this._buildAllAnnotations();
}
getAnnotations(
dictionaryData: { [p: string]: Dictionary },
currentUser: User,
viewMode: ViewMode,
areDevFeaturesEnabled: boolean,
): AnnotationData {
const entries: RedactionLogEntryWrapper[] = this._convertData(dictionaryData);
let allAnnotations = entries
.map(entry => AnnotationWrapper.fromData(entry))
.filter(ann => ann.manual || !this.file.excludedPages.includes(ann.pageNumber));
set redactionLog(redactionLog: IRedactionLog) {
this._redactionLog = redactionLog;
this._buildAllAnnotations();
}
if (!areDevFeaturesEnabled) {
allAnnotations = allAnnotations.filter(annotation => !annotation.isFalsePositive);
}
get redactionLog() {
return this._redactionLog;
}
const visibleAnnotations = allAnnotations.filter(annotation => {
getVisibleAnnotations(viewMode: ViewMode) {
return this.allAnnotations.filter(annotation => {
if (viewMode === 'STANDARD') {
return !annotation.isChangeLogRemoved;
} else if (viewMode === 'DELTA') {
@ -39,14 +39,33 @@ export class FileDataModel {
return annotation.isRedacted;
}
});
return {
visibleAnnotations: visibleAnnotations,
allAnnotations: allAnnotations,
};
}
private _convertData(dictionaryData: { [p: string]: Dictionary }): RedactionLogEntryWrapper[] {
private _buildAllAnnotations() {
const entries: RedactionLogEntryWrapper[] = 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(): RedactionLogEntryWrapper[] {
let result: RedactionLogEntryWrapper[] = [];
const reasonAnnotationIds: { [key: string]: RedactionLogEntryWrapper[] } = {};
@ -55,7 +74,7 @@ export class FileDataModel {
const redactionLogEntryWrapper: RedactionLogEntryWrapper = {};
Object.assign(redactionLogEntryWrapper, redactionLogEntry);
redactionLogEntryWrapper.type = redactionLogEntry.type;
redactionLogEntryWrapper.hintDictionary = dictionaryData[redactionLogEntry.type].hint;
redactionLogEntryWrapper.hintDictionary = this._dictionaryData[redactionLogEntry.type].hint;
this._isChangeLogEntry(redactionLogEntry, redactionLogEntryWrapper);

View File

@ -124,7 +124,7 @@
></iqser-circle-button>
<iqser-circle-button
(action)="suggestRemoveAnnotations($event, true)"
(action)="removeOrSuggestRemoveAnnotation($event, true)"
*ngIf="annotationPermissions.canRemoveOrSuggestToRemoveFromDictionary"
[tooltipPosition]="tooltipPosition"
[tooltip]="'annotation-actions.remove-annotation.remove-from-dict' | translate"
@ -144,7 +144,7 @@
></iqser-circle-button>
<iqser-circle-button
(action)="suggestRemoveAnnotations($event, false)"
(action)="removeOrSuggestRemoveAnnotation($event, false)"
*ngIf="annotationPermissions.canRemoveOrSuggestToRemoveOnlyHere"
[tooltipPosition]="tooltipPosition"
[tooltip]="'annotation-actions.remove-annotation.only-here' | translate"

View File

@ -77,9 +77,15 @@ export class AnnotationActionsComponent implements OnChanges {
this._setPermissions();
}
suggestRemoveAnnotations($event, removeFromDict: boolean) {
removeOrSuggestRemoveAnnotation($event, removeFromDict: boolean) {
$event.stopPropagation();
this.annotationActionsService.suggestRemoveAnnotation($event, this.annotations, this.file, removeFromDict, this.annotationsChanged);
this.annotationActionsService.removeOrSuggestRemoveAnnotation(
$event,
this.annotations,
this.file,
removeFromDict,
this.annotationsChanged,
);
}
markAsFalsePositive($event) {

View File

@ -1,4 +1,4 @@
import { ChangeDetectionStrategy, Component, EventEmitter, Input, OnChanges, OnDestroy, Output } from '@angular/core';
import { ChangeDetectionStrategy, ChangeDetectorRef, Component, EventEmitter, Input, OnChanges, OnDestroy, Output } from '@angular/core';
import { PermissionsService } from '@services/permissions.service';
import { ConfigService } from '@services/config.service';
import { DossiersService } from '@services/entity-services/dossiers.service';
@ -31,6 +31,7 @@ export class PageIndicatorComponent extends AutoUnsubscribe implements OnDestroy
private readonly _filesMapService: FilesMapService,
private readonly _dossiersService: DossiersService,
private readonly _configService: ConfigService,
private readonly _changeDetectorRef: ChangeDetectorRef,
private readonly _permissionService: PermissionsService,
) {
super();
@ -41,12 +42,16 @@ export class PageIndicatorComponent extends AutoUnsubscribe implements OnDestroy
}
private _setReadState() {
const readBefore = this.read;
const activePage = this.activePage;
if (!activePage) {
this.read = false;
} else {
// console.log('setting read to',activePage.showAsUnseen, !activePage.showAsUnseen);
this.read = !activePage.showAsUnseen;
}
// console.log(this.number, readBefore, activePage, this.read);
this._changeDetectorRef.detectChanges();
}
ngOnChanges() {

View File

@ -583,7 +583,7 @@ export class PdfViewerComponent implements OnInit, OnChanges {
try {
this.instance.UI.enableTools(['AnnotationCreateRectangle']);
} catch (e) {
console.log(e);
// happens
}
this.instance.UI.enableElements(elements);

View File

@ -26,12 +26,7 @@
<div class="vertical-line"></div>
<redaction-file-actions
#fileActions
(ocredFile)="ocredFile()"
[file]="file"
type="file-preview"
></redaction-file-actions>
<redaction-file-actions [file]="file" type="file-preview"></redaction-file-actions>
<iqser-circle-button
(action)="toggleFullScreen()"

View File

@ -1,6 +1,5 @@
import { ChangeDetectorRef, Component, HostListener, NgZone, OnDestroy, OnInit, TemplateRef, ViewChild } from '@angular/core';
import { ActivatedRoute, ActivatedRouteSnapshot, NavigationExtras, Router } from '@angular/router';
import { AppStateService } from '@state/app-state.service';
import { Core, WebViewerInstance } from '@pdftron/webviewer';
import { PdfViewerComponent } from './components/pdf-viewer/pdf-viewer.component';
import {
@ -19,14 +18,13 @@ import { MatDialogRef, MatDialogState } from '@angular/material/dialog';
import { ManualRedactionEntryWrapper } from '@models/file/manual-redaction-entry.wrapper';
import { AnnotationWrapper } from '@models/file/annotation.wrapper';
import { ManualAnnotationResponse } from '@models/file/manual-annotation-response';
import { AnnotationData, FileDataModel } from '@models/file/file-data.model';
import { FileDataModel } from '@models/file/file-data.model';
import { AnnotationDrawService } from '../../services/annotation-draw.service';
import { AnnotationProcessingService } from '../../services/annotation-processing.service';
import { Dossier, File, ViewMode } from '@red/domain';
import { PermissionsService } from '@services/permissions.service';
import { combineLatest, Observable, timer } from 'rxjs';
import { UserPreferenceService } from '@services/user-preference.service';
import { UserService } from '@services/user.service';
import { PdfViewerDataService } from '../../services/pdf-viewer-data.service';
import { download } from '@utils/file-download-utils';
import { FileWorkloadComponent } from './components/file-workload/file-workload.component';
@ -70,7 +68,6 @@ export class FilePreviewScreenComponent extends AutoUnsubscribe implements OnIni
fullScreen = false;
shouldDeselectAnnotationsOnPageChange = true;
fileData: FileDataModel;
annotationData: AnnotationData;
selectedAnnotations: AnnotationWrapper[] = [];
hideSkipped = false;
displayPdfViewer = false;
@ -84,7 +81,6 @@ export class FilePreviewScreenComponent extends AutoUnsubscribe implements OnIni
ready = false;
private _instance: WebViewerInstance;
private _lastPage: string;
private _reloadFileOnReanalysis = false;
@ViewChild('fileWorkloadComponent') private readonly _workloadComponent: FileWorkloadComponent;
@ViewChild('annotationFilterTemplate', {
read: TemplateRef,
@ -95,8 +91,6 @@ export class FilePreviewScreenComponent extends AutoUnsubscribe implements OnIni
constructor(
readonly permissionsService: PermissionsService,
readonly userPreferenceService: UserPreferenceService,
private readonly _appStateService: AppStateService,
private readonly _userService: UserService,
private readonly _watermarkService: WatermarkService,
private readonly _changeDetectorRef: ChangeDetectorRef,
private readonly _activatedRoute: ActivatedRoute,
@ -139,15 +133,13 @@ export class FilePreviewScreenComponent extends AutoUnsubscribe implements OnIni
}
get annotations(): AnnotationWrapper[] {
return this.annotationData ? this.annotationData.visibleAnnotations : [];
return this.fileData ? this.fileData.getVisibleAnnotations(this.viewModeService.viewMode) : [];
}
get activeViewer(): WebViewerInstance {
return this._instance;
}
private _setActiveViewerPage() {
const currentPage = this._instance?.Core.documentViewer?.getCurrentPage();
if (!currentPage) {
@ -169,7 +161,7 @@ export class FilePreviewScreenComponent extends AutoUnsubscribe implements OnIni
}
async updateViewMode(): Promise<void> {
const ocrAnnotationIds = this.annotationData.allAnnotations.filter(a => a.isOCR).map(a => a.id);
const ocrAnnotationIds = this.fileData.allAnnotations.filter(a => a.isOCR).map(a => a.id);
const annotations = this._getAnnotations(a => a.getCustomData('redact-manager'));
const redactions = annotations.filter(a => a.getCustomData('redaction'));
@ -251,18 +243,7 @@ export class FilePreviewScreenComponent extends AutoUnsubscribe implements OnIni
console.log(`[REDACTION] Delete previous annotations time: ${new Date().getTime() - startTime} ms`);
}
const processStartTime = new Date().getTime();
const dossier = this._dossiersService.find(this.dossierId);
const newAnnotationsData = this.fileData.getAnnotations(
this._appStateService.dictionaryData[dossier.dossierTemplateId],
this._userService.currentUser,
this.viewModeService.viewMode,
this.userPreferenceService.areDevFeaturesEnabled,
);
if (this.annotationData) {
this._setHiddenPropertyToNewAnnotations(newAnnotationsData.visibleAnnotations, this.annotationData.visibleAnnotations);
this._setHiddenPropertyToNewAnnotations(newAnnotationsData.allAnnotations, this.annotationData.allAnnotations);
}
this.annotationData = newAnnotationsData;
const annotationFilters = this._annotationProcessingService.getAnnotationFilter(this.annotations);
const primaryFilters = this._filterService.getGroup('primaryFilters')?.filters;
this._filterService.addFilterGroup({
@ -324,7 +305,7 @@ export class FilePreviewScreenComponent extends AutoUnsubscribe implements OnIni
response.manualRedactionEntryWrapper.rectId,
);
this._instance.Core.annotationManager.deleteAnnotation(annotation);
// await this._filesService.reload(this.dossierId, this.fileId).toPromise();
// await this._filesService.reload(this.dossierId, this.fileId).toPromise();
const distinctPages = manualRedactionEntryWrapper.manualRedactionEntry.positions
.map(p => p.page)
.filter((item, pos, self) => self.indexOf(item) === pos);
@ -431,10 +412,6 @@ export class FilePreviewScreenComponent extends AutoUnsubscribe implements OnIni
await this._reloadAnnotationsForPage(annotation?.pageNumber || this.activeViewerPage);
}
ocredFile(): void {
this._reloadFileOnReanalysis = true;
}
closeFullScreen() {
if (!!document.fullscreenElement && document.exitFullscreen) {
document.exitFullscreen().then();
@ -480,19 +457,10 @@ export class FilePreviewScreenComponent extends AutoUnsubscribe implements OnIni
}
private async _reloadFile(file: File): Promise<void> {
await this._loadFileData(file, true);
await this._loadFileData(file);
await this._stampPDF();
}
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 async _stampPDF() {
if (!this._instance) {
return;
@ -554,36 +522,26 @@ export class FilePreviewScreenComponent extends AutoUnsubscribe implements OnIni
this.addSubscription = this._filesMapService.fileReanalysed$
.pipe(filter(file => file.fileId === this.fileId))
.subscribe(async file => {
await this._loadFileData(file, !this._reloadFileOnReanalysis);
this._reloadFileOnReanalysis = false;
await this._reloadAnnotations();
if (file.lastProcessed !== this.fileData?.file.lastProcessed) {
await this._loadFileData(file);
await this._reloadAnnotations();
}
this._loadingService.stop();
});
}
private async _loadFileData(file: File, performUpdate = false): Promise<void | boolean> {
private async _loadFileData(file: File): Promise<void | boolean> {
if (!file || file.isError) {
return this._router.navigate([this._dossiersService.find(this.dossierId).routerLink]);
}
const fileData = await this._fileDownloadService.loadDataFor(file).toPromise();
const fileData = await this._fileDownloadService.loadDataFor(file, this.fileData).toPromise();
if (file.isPending) {
return;
}
if (performUpdate && !!this.fileData) {
this.fileData.redactionLog = fileData.redactionLog;
this.fileData.viewedPages = fileData.viewedPages;
const excludedOrIncludedPages = new Set(diff(this.fileData.file.excludedPages, file.excludedPages));
const currentPageAnnotations = this.annotations.filter(a => excludedOrIncludedPages.has(a.pageNumber));
this.fileData.file = file;
if (excludedOrIncludedPages?.size) {
await this._cleanupAndRedrawAnnotations(currentPageAnnotations, a => excludedOrIncludedPages.has(a.pageNumber));
}
} else {
this.fileData = fileData;
}
this.fileData = fileData;
}
@Debounce(0)
@ -600,6 +558,7 @@ export class FilePreviewScreenComponent extends AutoUnsubscribe implements OnIni
private async _reloadAnnotationsForPage(page: number) {
const currentPageAnnotations = this.annotations.filter(a => a.pageNumber === page);
this.fileData.file = await this._filesService.reload(this.dossierId, this.fileId).toPromise();
this.fileData.redactionLog = await this._fileDownloadService.loadRedactionLogFor(this.dossierId, this.fileId).toPromise();
await this._cleanupAndRedrawAnnotations(currentPageAnnotations, annotation => annotation.pageNumber === page);
@ -625,7 +584,7 @@ export class FilePreviewScreenComponent extends AutoUnsubscribe implements OnIni
}
}
private _redrawAnnotations(annotations = this.annotationData.allAnnotations) {
private _redrawAnnotations(annotations = this.fileData.allAnnotations) {
return this._annotationDrawService.drawAnnotations(
this._instance,
annotations,

View File

@ -113,7 +113,7 @@ export class AnnotationActionsService {
);
}
suggestRemoveAnnotation(
removeOrSuggestRemoveAnnotation(
$event: MouseEvent,
annotations: AnnotationWrapper[],
file: File,
@ -290,7 +290,7 @@ export class AnnotationActionsService {
title: this._translateService.instant('annotation-actions.remove-annotation.remove-from-dict'),
onClick: () => {
this._ngZone.run(() => {
this.suggestRemoveAnnotation(null, annotations, file, true, annotationsChanged);
this.removeOrSuggestRemoveAnnotation(null, annotations, file, true, annotationsChanged);
});
},
});
@ -405,7 +405,7 @@ export class AnnotationActionsService {
title: this._translateService.instant('annotation-actions.remove-annotation.only-here'),
onClick: () => {
this._ngZone.run(() => {
this.suggestRemoveAnnotation(null, annotations, file, false, annotationsChanged);
this.removeOrSuggestRemoveAnnotation(null, annotations, file, false, annotationsChanged);
});
},
});
@ -441,7 +441,7 @@ export class AnnotationActionsService {
viewer: WebViewerInstance,
file: File,
annotationWrapper: AnnotationWrapper,
annotationsChanged: EventEmitter<AnnotationWrapper>,
annotationsChanged?: EventEmitter<AnnotationWrapper>,
) {
const data = { dossier: this._dossier(file) };
this._dialogService.openDialog('resizeAnnotation', $event, data, async (result: { comment: string }) => {

View File

@ -7,14 +7,20 @@ import { File } from '@red/domain';
import { FileManagementService } from '@services/entity-services/file-management.service';
import { RedactionLogService } from './redaction-log.service';
import { ViewedPagesService } from '@services/entity-services/viewed-pages.service';
import { AppStateService } from '../../../state/app-state.service';
import { DossiersService } from '../../../services/entity-services/dossiers.service';
import { UserPreferenceService } from '../../../services/user-preference.service';
@Injectable()
export class PdfViewerDataService {
constructor(
private readonly _dossiersService: DossiersService,
private readonly _permissionsService: PermissionsService,
private readonly _fileManagementService: FileManagementService,
private readonly _redactionLogService: RedactionLogService,
private readonly _viewedPagesService: ViewedPagesService,
private readonly _appStateService: AppStateService,
private readonly _userPreferenceService: UserPreferenceService,
) {}
loadRedactionLogFor(dossierId: string, fileId: string) {
@ -24,12 +30,24 @@ export class PdfViewerDataService {
);
}
loadDataFor(file: File): Observable<FileDataModel> {
const file$ = this.downloadOriginalFile(file);
loadDataFor(file: File, fileData?: FileDataModel): Observable<FileDataModel> {
const file$ = fileData?.file.cacheIdentifier === file.cacheIdentifier ? of(fileData.fileData) : this.downloadOriginalFile(file);
const reactionLog$ = this.loadRedactionLogFor(file.dossierId, file.fileId);
const viewedPages$ = this.getViewedPagesFor(file);
return forkJoin([file$, reactionLog$, viewedPages$]).pipe(map(data => new FileDataModel(file, ...data)));
const dossier = this._dossiersService.find(file.dossierId);
return forkJoin([file$, reactionLog$, viewedPages$]).pipe(
map(
data =>
new FileDataModel(
file,
...data,
this._appStateService.dictionaryData[dossier.dossierTemplateId],
this._userPreferenceService.areDevFeaturesEnabled,
),
),
);
}
getViewedPagesFor(file: File) {

View File

@ -51,7 +51,6 @@ export class FileActionsComponent extends AutoUnsubscribe implements OnDestroy,
@Input() file: File;
@Input() type: 'file-preview' | 'dossier-overview-list' | 'dossier-overview-workflow';
@Input() maxWidth: number;
@Output() readonly ocredFile = new EventEmitter<void>();
toggleTooltip?: string;
assignTooltip?: string;
@ -308,7 +307,6 @@ export class FileActionsComponent extends AutoUnsubscribe implements OnDestroy,
$event.stopPropagation();
this._loadingService.start();
await this._reanalysisService.ocrFiles([this.file.fileId], this.file.dossierId).toPromise();
this.ocredFile.emit();
this._loadingService.stop();
}

View File

@ -15,7 +15,7 @@ export class PlatformSearchService extends GenericService<ISearchResponse> {
private readonly _dossiersService: DossiersService,
private readonly _filesMapService: FilesMapService,
) {
super(_injector, 'search');
super(_injector, 'search-v2');
}
search({ dossierIds, query }: ISearchInput): Observable<ISearchResponse> {
@ -29,7 +29,7 @@ export class PlatformSearchService extends GenericService<ISearchResponse> {
const body: ISearchRequest = {
dossierIds,
queryString: query ?? '',
page: 1,
page: 0,
returnSections: false,
pageSize: 300,
};

@ -1 +1 @@
Subproject commit 6527ccd3077bfcb4bedf729bf09edc5bd1449502
Subproject commit 3f1c419e0acdc92514b615611bdaa29e000a0b32

View File

@ -2,6 +2,8 @@ import { IFileAttributeConfig } from './file-attribute-config';
export interface IFileAttributesConfig {
delimiter?: string;
fileAttributeConfigs?: IFileAttributeConfig[];
encoding?: string;
filenameMappingColumnHeaderName?: string;
fileAttributeConfigs?: IFileAttributeConfig[];
}

View File

@ -93,9 +93,7 @@ export class File extends Entity<IFile> implements IFile {
this.hasSuggestions = !!file.hasSuggestions;
this.statusSort = StatusSorter[this.workflowStatus];
if (this.lastUpdated && this.lastOCRTime) {
this.cacheIdentifier = btoa((this.lastUploaded ?? '') + this.lastOCRTime);
}
this.cacheIdentifier = btoa((this.lastUploaded ?? '') + (this.lastOCRTime ?? ''));
this.hintsOnly = this.hasHints && !this.hasRedactions;
this.hasNone = !this.hasRedactions && !this.hasHints && !this.hasSuggestions;
this.isPending = this.processingStatus === ProcessingFileStatuses.UNPROCESSED;