Merge branch 'master' into VM/RED-3370

This commit is contained in:
Valentin Mihai 2022-02-18 10:15:42 +02:00
commit 306bbc8700
35 changed files with 227 additions and 80 deletions

View File

@ -311,6 +311,10 @@ export class AnnotationWrapper {
lastManualChange,
annotationWrapper.hintDictionary,
);
if (lastManualChange.annotationStatus === LogEntryStatus.REQUESTED) {
annotationWrapper.recategorizationType = lastManualChange.propertyChanges.type;
}
} else {
if (redactionLogEntryWrapper.recommendation) {
annotationWrapper.superType = 'recommendation';
@ -426,8 +430,17 @@ export class AnnotationWrapper {
case ManualRedactionType.RECATEGORIZE:
switch (lastManualChange.annotationStatus) {
case LogEntryStatus.APPROVED:
case LogEntryStatus.DECLINED:
return redactionLogEntry.redacted ? 'redaction' : 'hint';
case LogEntryStatus.DECLINED: {
if (redactionLogEntry.recommendation) {
return 'recommendation';
} else if (redactionLogEntry.redacted) {
return 'redaction';
} else if (redactionLogEntry.hint) {
return 'hint';
} else {
return 'skipped';
}
}
case LogEntryStatus.REQUESTED:
return 'suggestion-recategorize-image';
}

View File

@ -2,6 +2,7 @@ import { ChangeDetectionStrategy, Component, Inject } from '@angular/core';
import { IDossierState } from '@red/domain';
import { MAT_DIALOG_DATA, MatDialogRef } from '@angular/material/dialog';
import { FormBuilder, FormGroup } from '@angular/forms';
import { marker as _ } from '@biesbjerg/ngx-translate-extract-marker';
interface DialogData {
readonly toBeDeletedState: IDossierState;
@ -38,7 +39,7 @@ export class ConfirmDeleteDossierStateDialogComponent {
}
get label(): string {
return this.replaceDossierStatusId ? 'confirm-delete-dossier-state.delete-replace' : 'confirm-delete-dossier-state.delete';
return this.replaceDossierStatusId ? _('confirm-delete-dossier-state.delete-replace') : _('confirm-delete-dossier-state.delete');
}
get afterCloseValue(): string | true {

View File

@ -37,7 +37,7 @@ export class DossierDetailsStatsComponent implements OnInit {
switchMap(() => this._filesService.getDeletedFilesFor(this.dossier.id)),
map(files => files.length),
);
this.dossierTemplateName = this._dossierTemplatesService.find(this.dossier.dossierTemplateId).name;
this.dossierTemplateName = this._dossierTemplatesService.find(this.dossier.dossierTemplateId)?.name || '-';
}
openEditDossierDialog(section: string): void {

View File

@ -1,5 +1,11 @@
<div>
<div [class.error]="file.isError" [matTooltip]="file.filename" class="table-item-title" matTooltipPosition="above">
<div
[class.error]="file.isError"
[class.initial-processing]="file.isInitialProcessing"
[matTooltip]="file.filename"
class="table-item-title"
matTooltipPosition="above"
>
{{ file.filename }}
</div>
</div>

View File

@ -2,6 +2,14 @@
.table-item-title {
max-width: 25vw;
&.error {
color: var(--iqser-red-1);
}
&.initial-processing {
color: var(--iqser-disabled);
}
}
.primary-attribute {

View File

@ -40,15 +40,13 @@
<div [class.extend-cols]="file.isError" class="status-container cell">
<div *ngIf="file.isError" class="small-label error" translate="dossier-overview.file-listing.file-entry.file-error"></div>
<div *ngIf="file.isPending" class="small-label" translate="dossier-overview.file-listing.file-entry.file-pending"></div>
<div *ngIf="file.isUnprocessed" class="small-label" translate="dossier-overview.file-listing.file-entry.file-pending"></div>
<div *ngIf="file.isProcessing || file.canBeOpened" class="status-wrapper">
<div *ngIf="file.isProcessing" [matTooltip]="'file-status.processing' | translate" class="spinning-icon" matTooltipPosition="above">
<mat-icon svgIcon="red:reanalyse"></mat-icon>
</div>
<redaction-processing-indicator [file]="file"></redaction-processing-indicator>
<iqser-status-bar
*ngIf="!file.isError && !file.isPending"
*ngIf="!file.isError && !file.isUnprocessed && !file.isInitialProcessing"
[configs]="[
{
color: file.workflowStatus,

View File

@ -20,14 +20,7 @@
<redaction-file-workload [file]="file"></redaction-file-workload>
<div class="file-actions">
<div
*ngIf="file.isProcessing"
class="spinning-icon mr-8"
matTooltip="{{ 'file-status.processing' | translate }}"
matTooltipPosition="above"
>
<mat-icon svgIcon="red:reanalyse"></mat-icon>
</div>
<redaction-processing-indicator [file]="file" class="mr-8"></redaction-processing-indicator>
<div #actionsWrapper class="actions-wrapper">
<redaction-file-actions

View File

@ -36,6 +36,6 @@ export class DossiersListingDossierNameComponent {
}
getDossierTemplateNameFor(dossierTemplateId: string): string {
return this._dossierTemplatesService.find(dossierTemplateId).name;
return this._dossierTemplatesService.find(dossierTemplateId)?.name || '-';
}
}

View File

@ -1,11 +1,11 @@
<ng-container *ngIf="dossier.dossierStatusId">
<ng-container *ngIf="dossier.dossierStatusId && currentState">
<div class="flex-align-items-center dossier-status-container">
<div class="dossier-status-text">{{ currentState.name }}</div>
<redaction-small-chip [color]="currentState.color"></redaction-small-chip>
</div>
</ng-container>
<ng-container *ngIf="!dossier.dossierStatusId">
<ng-container *ngIf="!dossier.dossierStatusId || !currentState">
<div class="flex-align-items-center dossier-status-container">
<div class="dossier-status-text">{{ 'edit-dossier-dialog.general-info.form.dossier-status.placeholder' | translate }}</div>
<redaction-small-chip [color]="'#E2E4E9'"></redaction-small-chip>

View File

@ -143,7 +143,7 @@ export class ConfigService {
id =>
new NestedFilter({
id: id,
label: this._dossierTemplatesService.find(id).name,
label: this._dossierTemplatesService.find(id)?.name || '-',
}),
);

View File

@ -5,6 +5,7 @@ import { MultiSelectService } from '../../services/multi-select.service';
import { AnnotationReferencesService } from '../../services/annotation-references.service';
import { ViewModeService } from '../../services/view-mode.service';
import { FilePreviewStateService } from '../../services/file-preview-state.service';
import { UserPreferenceService } from '../../../../../../services/user-preference.service';
@Component({
selector: 'redaction-annotations-list',
@ -28,6 +29,7 @@ export class AnnotationsListComponent implements OnChanges {
readonly annotationReferencesService: AnnotationReferencesService,
private readonly _filterService: FilterService,
private readonly _state: FilePreviewStateService,
private readonly _userPreferenceService: UserPreferenceService,
) {}
ngOnChanges(changes: SimpleChanges): void {
@ -37,6 +39,10 @@ export class AnnotationsListComponent implements OnChanges {
}
annotationClicked(annotation: AnnotationWrapper, $event: MouseEvent): void {
if (this._userPreferenceService.areDevFeaturesEnabled) {
console.log('Selected Annotation:', annotation);
}
if (($event?.target as IqserEventTarget)?.localName === 'input') {
return;
}

View File

@ -2,7 +2,7 @@
<div #viewer [id]="(stateService.file$ | async).fileId" class="viewer"></div>
</div>
<input #compareFileInput (change)="uploadFile($event.target['files'])" class="file-upload-input" type="file" />
<input #compareFileInput (change)="uploadFile($event.target['files'])" class="file-upload-input" type="file" accept="application/pdf" />
<div *ngIf="utils?.totalPages && utils?.currentPage" class="pagination noselect">
<div (click)="utils.previousPage()">

View File

@ -6,14 +6,7 @@
</div>
<div class="flex-1 actions-container">
<div
*ngIf="file.isProcessing"
class="spinning-icon mr-16"
matTooltip="{{ 'file-status.processing' | translate }}"
matTooltipPosition="above"
>
<mat-icon svgIcon="red:reanalyse"></mat-icon>
</div>
<redaction-processing-indicator [file]="file" class="mr-16"></redaction-processing-indicator>
<redaction-user-management></redaction-user-management>

View File

@ -566,7 +566,7 @@ export class FilePreviewScreenComponent extends AutoUnsubscribe implements OnIni
return this._router.navigate([this._dossiersService.find(this.dossierId).routerLink]);
}
if (file.isPending) {
if (file.isUnprocessed) {
return;
}

View File

@ -1,16 +1,17 @@
<div *ngIf="isDossierOverviewList" class="action-buttons">
<ng-container *ngTemplateOutlet="actions"></ng-container>
<div
*ngIf="showStatusBar && file.isProcessing"
class="spinning-icon"
matTooltip="{{ 'file-status.processing' | translate }}"
matTooltipPosition="above"
>
<mat-icon svgIcon="red:reanalyse"></mat-icon>
</div>
<redaction-processing-indicator *ngIf="showStatusBar" [file]="file"></redaction-processing-indicator>
<iqser-status-bar *ngIf="showStatusBar" [configs]="[{ color: file.workflowStatus, length: 1 }]"></iqser-status-bar>
</div>
<input
#importRedactionsInput
(change)="importRedactions($event.target['files'])"
class="file-upload-input"
type="file"
accept="application/pdf"
/>
<ng-container *ngIf="isFilePreview || isDossierOverviewWorkflow">
<ng-container *ngTemplateOutlet="actions"></ng-container>
</ng-container>

View File

@ -10,6 +10,10 @@
}
}
.file-upload-input {
display: none;
}
.reviewer {
display: flex;
align-items: center;

View File

@ -2,6 +2,7 @@ import {
ChangeDetectionStrategy,
ChangeDetectorRef,
Component,
ElementRef,
HostBinding,
Input,
OnChanges,
@ -37,6 +38,9 @@ import { tap } from 'rxjs/operators';
import { DocumentInfoService } from '../../../screens/file-preview-screen/services/document-info.service';
import { ExpandableFileActionsComponent } from '@shared/components/expandable-file-actions/expandable-file-actions.component';
import { firstValueFrom } from 'rxjs';
import { environment } from '../../../../../../environments/environment';
import { loadCompareDocumentWrapper } from '../../../utils/compare-mode.utils';
import { RedactionImportService } from '../../services/redaction-import.service';
@Component({
selector: 'redaction-file-actions [file] [type]',
@ -53,12 +57,15 @@ export class FileActionsComponent extends AutoUnsubscribe implements OnDestroy,
@Input() maxWidth: number;
@Input() fileActionsHelpModeKey: 'document_features' | 'editor_document_features' = 'document_features';
@ViewChild('importRedactionsInput', { static: true }) importRedactionsInput: ElementRef;
toggleTooltip?: string;
assignTooltip?: string;
buttonType?: CircleButtonType;
showUndoApproval = false;
showAssignToSelf = false;
showImportRedactions = false;
showAssign = false;
showDelete = false;
showOCR = false;
@ -100,6 +107,7 @@ export class FileActionsComponent extends AutoUnsubscribe implements OnDestroy,
private readonly _reanalysisService: ReanalysisService,
private readonly _router: Router,
private readonly _changeRef: ChangeDetectorRef,
private readonly _redactionImportService: RedactionImportService,
) {
super();
}
@ -140,6 +148,13 @@ export class FileActionsComponent extends AutoUnsubscribe implements OnDestroy,
icon: 'red:assign-me',
show: this.showAssignToSelf,
},
{
type: ActionTypes.circleBtn,
action: $event => this._triggerImportRedactions($event),
tooltip: _('dossier-overview.import-redactions'),
icon: 'iqser:upload',
show: false, // this.showImportRedactions,
},
{
type: ActionTypes.downloadBtn,
show: true,
@ -272,6 +287,25 @@ export class FileActionsComponent extends AutoUnsubscribe implements OnDestroy,
);
}
private _triggerImportRedactions($event: MouseEvent) {
$event.stopPropagation();
this.importRedactionsInput.nativeElement.click();
}
async importRedactions(files: FileList) {
const fileToImport = files[0];
if (!fileToImport) {
console.error('No file to compare!');
return;
}
await firstValueFrom(this._redactionImportService.importRedactions(this.file.dossierId, this.file.fileId, fileToImport)).catch(
exception => {},
);
// reload file
}
forceReanalysisAction($event: LongPressEvent) {
this.analysisForced = !$event.touchEnd && this._userPreferenceService.areDevFeaturesEnabled;
this._setup();
@ -374,13 +408,15 @@ export class FileActionsComponent extends AutoUnsubscribe implements OnDestroy,
this.canDisableAutoAnalysis = this._permissionsService.canDisableAutoAnalysis([this.file]);
this.canEnableAutoAnalysis = this._permissionsService.canEnableAutoAnalysis([this.file]);
this.showStatusBar = !this.file.isError && !this.file.isPending && this.isDossierOverviewList;
this.showStatusBar = !this.file.isError && !this.file.isUnprocessed && this.isDossierOverviewList;
this.showAssignToSelf = this._permissionsService.canAssignToSelf(this.file) && this.isDossierOverview;
this.showAssign =
(this._permissionsService.canAssignUser(this.file) || this._permissionsService.canUnassignUser(this.file)) &&
this.isDossierOverview;
this.showImportRedactions = this._permissionsService.canImportRedactions(this.file);
this.showReanalyseFilePreview = this.canReanalyse && this.isFilePreview && (this.analysisForced || this.canEnableAutoAnalysis);
this.showReanalyseDossierOverview =
this.canReanalyse && this.isDossierOverview && (this.analysisForced || this.canEnableAutoAnalysis);

View File

@ -0,0 +1,25 @@
import { Injectable, Injector } from '@angular/core';
import { GenericService, HeadersConfiguration, RequiredParam, Validate } from '@iqser/common-ui';
@Injectable()
export class RedactionImportService extends GenericService<void> {
constructor(protected readonly _injector: Injector) {
super(_injector, 'import-redactions');
}
@Validate()
importRedactions(@RequiredParam() dossierId: string, @RequiredParam() fileId: string, file?: Blob) {
const formParams = new FormData();
if (file !== undefined) {
formParams.append('file', file);
}
const headers = HeadersConfiguration.getHeaders({ contentType: false }).append('ngsw-bypass', 'true');
return this._http.post<void>(`/${this._defaultModelPath}/${dossierId}/${fileId}`, formParams, {
headers,
observe: 'response',
});
}
}

View File

@ -4,13 +4,14 @@ import { FileAssignService } from './services/file-assign.service';
import { FileActionsComponent } from './components/file-actions/file-actions.component';
import { IqserIconsModule } from '@iqser/common-ui';
import { SharedModule } from '@shared/shared.module';
import { RedactionImportService } from './services/redaction-import.service';
const components = [FileActionsComponent];
@NgModule({
declarations: [...components],
exports: [...components],
providers: [FileAssignService],
providers: [FileAssignService, RedactionImportService],
imports: [CommonModule, IqserIconsModule, SharedModule],
})
export class SharedDossiersModule {}

View File

@ -10,6 +10,8 @@ export const workflowFileStatusTranslations: { [key in WorkflowFileStatus]: stri
};
export const processingFileStatusTranslations: { [key in ProcessingFileStatus]: string } = {
ANALYSE: _('file-status.analyse'),
NER_ANALYZING: _('file-status.ner-analyzing'),
PROCESSED: _('file-status.processed'),
DELETED: _('file-status.deleted'),
ERROR: _('file-status.error'),

View File

@ -0,0 +1,3 @@
<div *ngIf="file.isProcessing" [matTooltip]="tooltip | translate" class="spinning-icon" matTooltipPosition="above">
<mat-icon svgIcon="red:reanalyse"></mat-icon>
</div>

View File

@ -0,0 +1,3 @@
:host {
display: block;
}

View File

@ -0,0 +1,18 @@
import { ChangeDetectionStrategy, Component, Input, OnChanges } from '@angular/core';
import { File } from '@red/domain';
import { marker as _ } from '@biesbjerg/ngx-translate-extract-marker';
@Component({
selector: 'redaction-processing-indicator [file]',
templateUrl: './processing-indicator.component.html',
styleUrls: ['./processing-indicator.component.scss'],
changeDetection: ChangeDetectionStrategy.OnPush,
})
export class ProcessingIndicatorComponent implements OnChanges {
tooltip: string;
@Input() file: File;
ngOnChanges() {
this.tooltip = this.file.isInitialProcessing ? _('file-status.initial-processing') : _('file-status.re-processing');
}
}

View File

@ -26,6 +26,7 @@ import { TypeFilterComponent } from './components/type-filter/type-filter.compon
import { TeamMembersComponent } from './components/team-members/team-members.component';
import { EditorComponent } from './components/editor/editor.component';
import { ExpandableFileActionsComponent } from './components/expandable-file-actions/expandable-file-actions.component';
import { ProcessingIndicatorComponent } from '@shared/components/processing-indicator/processing-indicator.component';
const buttons = [FileDownloadBtnComponent, UserButtonComponent];
@ -41,6 +42,7 @@ const components = [
TypeFilterComponent,
TeamMembersComponent,
ExpandableFileActionsComponent,
ProcessingIndicatorComponent,
...buttons,
];

View File

@ -15,6 +15,6 @@ export class DictionariesMapService extends EntitiesMapService<Dictionary, IDict
}
getDictionaryColor(type: string, dossierTemplateId: string) {
return !this.get(dossierTemplateId) ? '#cccccc' : this.getDictionary(type, dossierTemplateId).hexColor;
return !this.get(dossierTemplateId) ? '#cccccc' : this.getDictionary(type, dossierTemplateId)?.hexColor || '#cccccc';
}
}

View File

@ -3,7 +3,7 @@ import { EntitiesService, mapEach, RequiredParam, Validate } from '@iqser/common
import { DossierState, IDossierState } from '@red/domain';
import { forkJoin, Observable, switchMap } from 'rxjs';
import { DossierTemplatesService } from '@services/entity-services/dossier-templates.service';
import { map, tap } from 'rxjs/operators';
import { defaultIfEmpty, map, tap } from 'rxjs/operators';
@Injectable({
providedIn: 'root',
@ -27,7 +27,7 @@ export class DossierStateService extends EntitiesService<DossierState, IDossierS
return this._dossierTemplatesService.all$.pipe(
mapEach(template => template.dossierTemplateId),
mapEach(id => this.loadAllForTemplate(id)),
switchMap(all => forkJoin(all)),
switchMap(all => forkJoin(all).pipe(defaultIfEmpty([]))),
map(value => value.flatMap(item => item)),
tap(value => this.setEntities(value)),
);

View File

@ -1,7 +1,7 @@
import { EntitiesService, List, mapEach, RequiredParam, Toaster, Validate } from '@iqser/common-ui';
import { DossierTemplate, IDossierTemplate } from '@red/domain';
import { Injectable, Injector } from '@angular/core';
import { forkJoin, Observable, throwError } from 'rxjs';
import { forkJoin, map, Observable, of, throwError } from 'rxjs';
import { FileAttributesService } from './file-attributes.service';
import { catchError, mapTo, switchMap, tap } from 'rxjs/operators';
import { DossierTemplateStatsService } from '@services/entity-services/dossier-template-stats.service';
@ -34,13 +34,17 @@ export class DossierTemplatesService extends EntitiesService<DossierTemplate, ID
return this.getAll().pipe(
mapEach(entity => new DossierTemplate(entity)),
/* Load stats before updating entities */
switchMap(templates =>
forkJoin([
this._dossierTemplateStatsService.getFor(dossierTemplateIds(templates)),
...getAttributes(templates),
this._dictionaryService.loadDictionaryData(dossierTemplateIds(templates)),
]).pipe(mapTo(templates)),
),
switchMap(templates => {
if (templates.length) {
return forkJoin([
this._dossierTemplateStatsService.getFor(dossierTemplateIds(templates)),
...getAttributes(templates),
this._dictionaryService.loadDictionaryData(dossierTemplateIds(templates)),
]).pipe(mapTo(templates));
} else {
return of(templates);
}
}),
tap(templates => this.setEntities(templates)),
);
}

View File

@ -2,7 +2,7 @@ import { Injectable, Injector } from '@angular/core';
import { EntitiesService, List, mapEach, QueryParam, RequiredParam, shareLast, Toaster, Validate } from '@iqser/common-ui';
import { Dossier, IDossier, IDossierRequest } from '@red/domain';
import { catchError, filter, map, mapTo, switchMap, tap } from 'rxjs/operators';
import { combineLatest, firstValueFrom, Observable, of, Subject, throwError, timer } from 'rxjs';
import { combineLatest, firstValueFrom, forkJoin, Observable, of, Subject, throwError, timer } from 'rxjs';
import { marker as _ } from '@biesbjerg/ngx-translate-extract-marker';
import { HttpErrorResponse, HttpStatusCode } from '@angular/common/http';
import { DossierStatsService } from '@services/entity-services/dossier-stats.service';
@ -14,14 +14,16 @@ export interface IDossiersStats {
totalAnalyzedPages: number;
}
interface DossierChange {
readonly dossierChanges: boolean;
readonly dossierId: string;
readonly fileChanges: boolean;
}
type DossierChanges = readonly DossierChange[];
interface ChangesDetails {
dossierChanges: [
{
dossierChanges: boolean;
dossierId: string;
fileChanges: boolean;
},
];
readonly dossierChanges: DossierChanges;
}
const DOSSIER_EXISTS_MSG = _('add-dossier-dialog.errors.dossier-already-exists');
@ -31,7 +33,7 @@ const GENERIC_MSG = _('add-dossier-dialog.errors.generic');
providedIn: 'root',
})
export class DossiersService extends EntitiesService<Dossier, IDossier> {
readonly generalStats$ = this.all$.pipe(switchMap(entities => this._generalStats$(entities)));
readonly generalStats$ = this.all$.pipe(switchMap(entities => this.#generalStats$(entities)));
readonly dossierFileChanges$ = new Subject<string>();
constructor(
@ -44,8 +46,8 @@ export class DossiersService extends EntitiesService<Dossier, IDossier> {
timer(CHANGED_CHECK_INTERVAL, CHANGED_CHECK_INTERVAL)
.pipe(
switchMap(() => this.loadAllIfChanged()),
tap(changes => this._emitFileChanges(changes)),
switchMap(() => this.loadOnlyChanged()),
tap(changes => this.#emitFileChanges(changes)),
)
.subscribe();
}
@ -61,8 +63,13 @@ export class DossiersService extends EntitiesService<Dossier, IDossier> {
);
}
loadAllIfChanged(): Observable<ChangesDetails> {
return this.hasChangesDetails$().pipe(switchMap(changes => this.loadAll().pipe(mapTo(changes))));
loadOnlyChanged(): Observable<ChangesDetails> {
const load = (changes: DossierChanges) => forkJoin(changes.map(change => this._load(change.dossierId)));
return this.hasChangesDetails$().pipe(
switchMap(changes => load(changes.dossierChanges).pipe(mapTo(changes))),
tap(() => this._updateLastChanged()),
);
}
hasChangesDetails$(): Observable<ChangesDetails> {
@ -115,11 +122,19 @@ export class DossiersService extends EntitiesService<Dossier, IDossier> {
return this.all.filter(dossier => dossier.dossierStatusId === dossierStatusId).length;
}
private _emitFileChanges(changes: ChangesDetails): void {
private _load(id: string, queryParams?: List<QueryParam>): Observable<Dossier> {
return super._getOne([id], this._defaultModelPath, queryParams).pipe(
map(entity => new Dossier(entity)),
switchMap(dossier => this._dossierStatsService.getFor([dossier.dossierId]).pipe(mapTo(dossier))),
tap(dossier => this.replace(dossier)),
);
}
#emitFileChanges(changes: ChangesDetails): void {
changes.dossierChanges.filter(change => change.fileChanges).forEach(change => this.dossierFileChanges$.next(change.dossierId));
}
private _computeStats(entities: List<Dossier>): IDossiersStats {
#computeStats(entities: List<Dossier>): IDossiersStats {
let totalAnalyzedPages = 0;
const totalPeople = new Set<string>();
@ -134,11 +149,11 @@ export class DossiersService extends EntitiesService<Dossier, IDossier> {
};
}
private _generalStats$(entities: List<Dossier>): Observable<IDossiersStats> {
#generalStats$(entities: List<Dossier>): Observable<IDossiersStats> {
const stats$ = entities.map(entity => this._dossierStatsService.watch$(entity.dossierId));
return combineLatest(stats$).pipe(
filter(stats => stats.every(s => !!s)),
map(() => this._computeStats(entities)),
map(() => this.#computeStats(entities)),
shareLast(),
);
}

View File

@ -154,6 +154,10 @@ export class PermissionsService {
return (comment.user === this._userService.currentUser.id || this.isApprover(dossier)) && !file.isApproved;
}
canImportRedactions(file: File) {
return (this.isFileAssignee(file) || this.isApprover(this._getDossier(file))) && !file.isApproved;
}
private _canToggleAnalysis(file: File): boolean {
return this.isFileAssignee(file) && (file.isNew || file.isUnderReview || file.isUnderApproval);
}

View File

@ -1,7 +1,7 @@
{
"ADMIN_CONTACT_NAME": null,
"ADMIN_CONTACT_URL": null,
"API_URL": "https://dev-04.iqser.cloud/redaction-gateway-v1",
"API_URL": "https://rosa1.iqser.cloud/redaction-gateway-v1",
"APP_NAME": "RedactManager",
"AUTO_READ_TIME": 3,
"BACKEND_APP_VERSION": "4.4.40",
@ -17,7 +17,7 @@
"MAX_RETRIES_ON_SERVER_ERROR": 3,
"OAUTH_CLIENT_ID": "redaction",
"OAUTH_IDP_HINT": null,
"OAUTH_URL": "https://dev-04.iqser.cloud/auth/realms/redaction",
"OAUTH_URL": "https://rosa1.iqser.cloud/auth/realms/redaction",
"RECENT_PERIOD_IN_HOURS": 24,
"SELECTION_MODE": "structural",
"MANUAL_BASE_URL": "https://docs.redactmanager.com"

View File

@ -410,8 +410,8 @@
"configurations": "Configurations",
"confirm-delete-dossier-state": {
"cancel": "Cancel",
"delete-replace": "Delete and Replace",
"delete": "Delete only",
"delete-replace": "Delete and Replace",
"form": {
"status": "Replace Status",
"status-placeholder": "Choose another status"
@ -733,6 +733,7 @@
"assign-approver": "Assign Approver",
"assign-me": "Assign To Me",
"assign-reviewer": "Assign User",
"import-redactions": "Import redactions from other file",
"bulk": {
"delete": "Delete Documents",
"reanalyse": "Analyze Documents"
@ -910,12 +911,12 @@
},
"download-type": {
"annotated": "Annotated PDF",
"delta-preview": "Delta PDF",
"flatten": "Flatten PDF",
"label": "{length} document {length, plural, one{version} other{versions}}",
"original": "Optimized PDF",
"preview": "Preview PDF",
"redacted": "Redacted PDF",
"delta-preview": "Delta PDF"
"redacted": "Redacted PDF"
},
"downloads-list": {
"actions": {
@ -1248,16 +1249,20 @@
}
},
"file-status": {
"analyse": "Analyzing",
"approved": "Approved",
"deleted": "Deleted",
"error": "Re-processing required",
"full-reprocess": "Processing",
"image-analyzing": "Image Analyzing",
"indexing": "Processing",
"initial-processing": "Initial processing...",
"ner-analyzing": "NER Analyzing",
"new": "New",
"ocr-processing": "OCR Processing",
"processed": "Processed",
"processing": "Processing...",
"re-processing": "Re-processing...",
"reprocess": "Processing",
"unassigned": "Unassigned",
"under-approval": "Under Approval",

View File

@ -48,8 +48,9 @@ export class File extends Entity<IFile> implements IFile {
readonly isNew: boolean;
readonly isError: boolean;
readonly isProcessing: boolean;
readonly isInitialProcessing: boolean;
readonly isApproved: boolean;
readonly isPending: boolean;
readonly isUnprocessed: boolean;
readonly isUnderReview: boolean;
readonly isUnderApproval: boolean;
readonly canBeApproved: boolean;
@ -89,6 +90,7 @@ export class File extends Entity<IFile> implements IFile {
this.processingStatus = file.processingStatus;
this.workflowStatus = file.workflowStatus;
this.isError = this.processingStatus === ProcessingFileStatuses.ERROR;
this.isUnprocessed = this.processingStatus === ProcessingFileStatuses.UNPROCESSED;
this.numberOfPages = this.isError ? 0 : file.numberOfPages ?? 0;
this.rulesVersion = file.rulesVersion;
this.uploader = file.uploader;
@ -99,14 +101,14 @@ export class File extends Entity<IFile> implements IFile {
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;
this.isProcessing = isProcessingStatuses.includes(this.processingStatus);
this.isInitialProcessing = this.isProcessing && this.numberOfAnalyses === 0;
this.isApproved = this.workflowStatus === WorkflowFileStatuses.APPROVED;
this.isNew = this.workflowStatus === WorkflowFileStatuses.NEW;
this.isUnderReview = this.workflowStatus === WorkflowFileStatuses.UNDER_REVIEW;
this.isUnderApproval = this.workflowStatus === WorkflowFileStatuses.UNDER_APPROVAL;
this.canBeApproved = !this.analysisRequired && !this.hasSuggestions && !this.isProcessing && !this.isError;
this.canBeOpened = !this.isError && !this.isPending && this.numberOfAnalyses > 0;
this.canBeOpened = !this.isError && !this.isUnprocessed && this.numberOfAnalyses > 0;
this.canBeOCRed = !this.excluded && !this.lastOCRTime && (this.isNew || this.isUnderReview || this.isUnderApproval);
if (!this.fileAttributes || !this.fileAttributes.attributeIdToValue) {

View File

@ -11,16 +11,18 @@ export const WorkflowFileStatuses = {
export type WorkflowFileStatus = keyof typeof WorkflowFileStatuses;
export const ProcessingFileStatuses = {
ANALYSE: 'ANALYSE',
DELETED: 'DELETED',
ERROR: 'ERROR',
FULLREPROCESS: 'FULLREPROCESS',
IMAGE_ANALYZING: 'IMAGE_ANALYZING',
SURROUNDING_TEXT_PROCESSING: 'SURROUNDING_TEXT_PROCESSING',
INDEXING: 'INDEXING',
NER_ANALYZING: 'NER_ANALYZING',
OCR_PROCESSING: 'OCR_PROCESSING',
PROCESSED: 'PROCESSED',
PROCESSING: 'PROCESSING',
REPROCESS: 'REPROCESS',
SURROUNDING_TEXT_PROCESSING: 'SURROUNDING_TEXT_PROCESSING',
UNPROCESSED: 'UNPROCESSED',
} as const;
@ -32,8 +34,10 @@ export const isProcessingStatuses: List<ProcessingFileStatus> = [
ProcessingFileStatuses.SURROUNDING_TEXT_PROCESSING,
ProcessingFileStatuses.OCR_PROCESSING,
ProcessingFileStatuses.IMAGE_ANALYZING,
ProcessingFileStatuses.NER_ANALYZING,
ProcessingFileStatuses.INDEXING,
ProcessingFileStatuses.PROCESSING,
ProcessingFileStatuses.ANALYSE,
] as const;
export interface StatusBarConfig {

View File

@ -1,6 +1,6 @@
{
"name": "redaction",
"version": "3.236.0",
"version": "3.243.0",
"private": true,
"license": "MIT",
"scripts": {

Binary file not shown.