RED-10405: added labels and filtering for separate file pending types.

This commit is contained in:
Nicoleta Panaghiu 2024-11-13 10:29:34 +02:00
parent e9bd45c7cc
commit b05024cb99
13 changed files with 125 additions and 36 deletions

View File

@ -61,7 +61,7 @@
*ngFor="let config of statusConfig" *ngFor="let config of statusConfig"
[attr.help-mode-key]="'dashboard_in_dossier'" [attr.help-mode-key]="'dashboard_in_dossier'"
[config]="config" [config]="config"
filterKey="processingTypeFilters" [filterKey]="PendingTypes[config.id] ? 'pendingTypeFilters' : 'processingTypeFilters'"
></iqser-progress-bar> ></iqser-progress-bar>
</div> </div>

View File

@ -21,7 +21,9 @@ import {
DossierAttributeWithValue, DossierAttributeWithValue,
DossierStats, DossierStats,
File, File,
FileErrorCodes,
IDossierRequest, IDossierRequest,
PendingTypes,
ProcessingTypes, ProcessingTypes,
StatusSorter, StatusSorter,
User, User,
@ -74,6 +76,7 @@ export class DossierDetailsComponent extends ContextComponent<DossierDetailsCont
#currentChartSubtitleIndex = 0; #currentChartSubtitleIndex = 0;
readonly #dossierId = getParam(DOSSIER_ID); readonly #dossierId = getParam(DOSSIER_ID);
protected readonly circleButtonTypes = CircleButtonTypes; protected readonly circleButtonTypes = CircleButtonTypes;
protected readonly PendingTypes = PendingTypes;
@Input() dossierAttributes: DossierAttributeWithValue[]; @Input() dossierAttributes: DossierAttributeWithValue[];
@Output() readonly toggleCollapse = new EventEmitter(); @Output() readonly toggleCollapse = new EventEmitter();
editingOwner = false; editingOwner = false;
@ -153,6 +156,10 @@ export class DossierDetailsComponent extends ContextComponent<DossierDetailsCont
} }
#calculateStatusConfig(stats: DossierStats): ProgressBarConfigModel[] { #calculateStatusConfig(stats: DossierStats): ProgressBarConfigModel[] {
const files = this._filesMapService.get(this.#dossierId);
const numberOfRulesLockedFiles = files.filter(file => file.errorCode === FileErrorCodes.LOCKED_RULES).length;
const numberOfTimeoutFiles = files.filter(file => file.errorCode === FileErrorCodes.RULES_EXECUTION_TIMEOUT).length;
const numberOfUnknownErrorFiles = stats.processingStats.pending - numberOfRulesLockedFiles - numberOfTimeoutFiles;
return [ return [
{ {
id: ProcessingTypes.pending, id: ProcessingTypes.pending,
@ -161,6 +168,27 @@ export class DossierDetailsComponent extends ContextComponent<DossierDetailsCont
count: stats.processingStats.pending, count: stats.processingStats.pending,
icon: 'red:reanalyse', icon: 'red:reanalyse',
}, },
{
id: PendingTypes.lockedRules,
label: _('processing-status.pending-locked-rules'),
total: stats.numberOfFiles,
count: numberOfRulesLockedFiles,
icon: 'red:reanalyse',
},
{
id: PendingTypes.timeout,
label: _('processing-status.pending-timeout'),
total: stats.numberOfFiles,
count: numberOfTimeoutFiles,
icon: 'red:reanalyse',
},
{
id: PendingTypes.unknown,
label: _('processing-status.pending-unknown'),
total: stats.numberOfFiles,
count: numberOfUnknownErrorFiles,
icon: 'red:reanalyse',
},
{ {
id: ProcessingTypes.ocr, id: ProcessingTypes.ocr,
label: _('processing-status.ocr'), label: _('processing-status.ocr'),

View File

@ -39,7 +39,12 @@
</ng-container> </ng-container>
<div [class.extend-cols]="file.isError" class="status-container cell"> <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.isError"
class="small-label error"
translate="dossier-overview.file-listing.file-entry.file-error"
[translateParams]="{ errorCode: file.errorCode }"
></div>
<div *ngIf="file.isUnprocessed" 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>

View File

@ -24,6 +24,7 @@ import {
FileAttributeConfigType, FileAttributeConfigType,
FileAttributeConfigTypes, FileAttributeConfigTypes,
IFileAttributeConfig, IFileAttributeConfig,
PendingType,
ProcessingType, ProcessingType,
StatusSorter, StatusSorter,
User, User,
@ -184,6 +185,7 @@ export class ConfigService {
const allDistinctPeople = new Set<string>(); const allDistinctPeople = new Set<string>();
const allDistinctNeedsWork = new Set<string>(); const allDistinctNeedsWork = new Set<string>();
const allDistinctProcessingTypes = new Set<ProcessingType>(); const allDistinctProcessingTypes = new Set<ProcessingType>();
const allDistinctPendingTypes = new Set<PendingType>();
const dynamicFilters = new Map<string, { type: FileAttributeConfigType; filterValue: Set<string> }>(); const dynamicFilters = new Map<string, { type: FileAttributeConfigType; filterValue: Set<string> }>();
@ -216,6 +218,7 @@ export class ConfigService {
} }
allDistinctProcessingTypes.add(file.processingType); allDistinctProcessingTypes.add(file.processingType);
allDistinctPendingTypes.add(file.pendingType);
// extract values for dynamic filters // extract values for dynamic filters
fileAttributeConfigs.forEach(config => { fileAttributeConfigs.forEach(config => {
@ -317,6 +320,14 @@ export class ConfigService {
hide: true, hide: true,
}); });
const pendingTypesFilters = [...allDistinctPendingTypes].map(item => new NestedFilter({ id: item, label: item }));
filterGroups.push({
slug: 'pendingTypeFilters',
filters: pendingTypesFilters,
checker: (file: File, filter: INestedFilter) => file.pendingType === filter.id,
hide: true,
});
dynamicFilters.forEach((value: { filterValue: Set<string>; type: FileAttributeConfigType }, filterKey: string) => { dynamicFilters.forEach((value: { filterValue: Set<string>; type: FileAttributeConfigType }, filterKey: string) => {
const id = filterKey.split(':')[0]; const id = filterKey.split(':')[0];
const key = filterKey.split(':')[1]; const key = filterKey.split(':')[1];

View File

@ -983,7 +983,7 @@
"download-file-disabled": "Download: Sie müssen Genehmiger im Dossier sein und die initiale Verarbeitung {count, plural, one{der Datei} other{der Dateien}} muss abgeschlossen sein.", "download-file-disabled": "Download: Sie müssen Genehmiger im Dossier sein und die initiale Verarbeitung {count, plural, one{der Datei} other{der Dateien}} muss abgeschlossen sein.",
"file-listing": { "file-listing": {
"file-entry": { "file-entry": {
"file-error": "Reanalyse erforderlich", "file-error": "Reanalyse erforderlich ({errorCode, select, RULES_EXECUTION_TIMEOUT{Zeitlimit für Regeln} LOCKED_RULES{Regeln gesperrt} other{unbekannt}})",
"file-pending": "Ausstehend ..." "file-pending": "Ausstehend ..."
} }
}, },
@ -1459,7 +1459,7 @@
"save": { "save": {
"error": "Erstellung der Datei-Attribute fehlgeschlagen.", "error": "Erstellung der Datei-Attribute fehlgeschlagen.",
"label": "Attribute speichern", "label": "Attribute speichern",
"success": "{count} Datei-{count, plural, one{Attribut} other{Attribute}} erfolgreich erstellt!" "success": "{count} Datei-{count, plural, one{Attribut} other{Attribute}} erfolgreich erstellt."
}, },
"search": { "search": {
"placeholder": "Nach Spaltennamen suchen..." "placeholder": "Nach Spaltennamen suchen..."
@ -2097,6 +2097,9 @@
"processing-status": { "processing-status": {
"ocr": "OCR", "ocr": "OCR",
"pending": "Ausstehend", "pending": "Ausstehend",
"pending-locked-rules": "Ausstehend (Regeln gesperrt)",
"pending-timeout": "Ausstehend (Zeitlimit für Regeln)",
"pending-unknown": "Ausstehend (unbekannt)",
"processed": "Verarbeitet", "processed": "Verarbeitet",
"processing": "Verarbeitung läuft" "processing": "Verarbeitung läuft"
}, },
@ -2365,8 +2368,8 @@
"inactive": "Inaktiv", "inactive": "Inaktiv",
"manager-admin": "Manager & Admin", "manager-admin": "Manager & Admin",
"no-role": "Keine Rolle definiert", "no-role": "Keine Rolle definiert",
"red-admin": "Anwendungsadmin", "red-admin": "{count, plural, one{Anwendungsadmin} other{Anwendungsadmins}}",
"red-manager": "Manager", "red-manager": "{count, plural, one{Manager} other{Manager}}",
"red-user": "Benutzer", "red-user": "Benutzer",
"red-user-admin": "Benutzeradmin", "red-user-admin": "Benutzeradmin",
"regular": "regulärer Benutzer" "regular": "regulärer Benutzer"

View File

@ -983,7 +983,7 @@
"download-file-disabled": "To download, ensure you are an approver in the dossier, and the {count, plural, one{file has undergone} other{files have undergone}} initial processing.", "download-file-disabled": "To download, ensure you are an approver in the dossier, and the {count, plural, one{file has undergone} other{files have undergone}} initial processing.",
"file-listing": { "file-listing": {
"file-entry": { "file-entry": {
"file-error": "Re-processing required", "file-error": "Re-processing required ({errorCode, select, RULES_EXECUTION_TIMEOUT{Rules timeout} LOCKED_RULES{Rules locked} other{unknown}})",
"file-pending": "Pending..." "file-pending": "Pending..."
} }
}, },
@ -2097,6 +2097,9 @@
"processing-status": { "processing-status": {
"ocr": "OCR", "ocr": "OCR",
"pending": "Pending", "pending": "Pending",
"pending-locked-rules": "Pending (Rules locked)",
"pending-timeout": "Pending (Rules timeout)",
"pending-unknown": "Pending (unknown)",
"processed": "Processed", "processed": "Processed",
"processing": "Processing" "processing": "Processing"
}, },

View File

@ -983,7 +983,7 @@
"download-file-disabled": "Download: Sie müssen Genehmiger im Dossier sein und die initiale Verarbeitung {count, plural, one{der Datei} other{der Dateien}} muss abgeschlossen sein.", "download-file-disabled": "Download: Sie müssen Genehmiger im Dossier sein und die initiale Verarbeitung {count, plural, one{der Datei} other{der Dateien}} muss abgeschlossen sein.",
"file-listing": { "file-listing": {
"file-entry": { "file-entry": {
"file-error": "Reanalyse erforderlich", "file-error": "Reanalyse erforderlich ({errorCode, select, RULES_EXECUTION_TIMEOUT{Zeitlimit für Regeln} LOCKED_RULES{Regeln gesperrt} other{unbekannt}})",
"file-pending": "Ausstehend ..." "file-pending": "Ausstehend ..."
} }
}, },
@ -1381,7 +1381,7 @@
}, },
"file": { "file": {
"action": "Zurück zum Dossier", "action": "Zurück zum Dossier",
"label": "Diese Datei wurde gelöscht!" "label": "Diese Datei wurde gelöscht."
} }
}, },
"file-preview": { "file-preview": {
@ -2097,6 +2097,9 @@
"processing-status": { "processing-status": {
"ocr": "OCR", "ocr": "OCR",
"pending": "Ausstehend", "pending": "Ausstehend",
"pending-locked-rules": "Ausstehend (Regeln gesperrt)",
"pending-timeout": "Ausstehend (Zeitlimit für Regeln)",
"pending-unknown": "Ausstehend (unbekannt)",
"processed": "Verarbeitet", "processed": "Verarbeitet",
"processing": "Verarbeitung läuft" "processing": "Verarbeitung läuft"
}, },
@ -2288,7 +2291,7 @@
} }
} }
}, },
"invalid-upload": "Ungültiges Upload-Format ausgewählt! Unterstützt werden Dokumente im .xlsx- und im .docx-Format", "invalid-upload": "Ungültiges Upload-Format ausgewählt. Unterstützte Formate: .xlsx- und .docx",
"multi-file-report": "(Mehrere Dateien)", "multi-file-report": "(Mehrere Dateien)",
"report-documents": "Berichtsvorlagen", "report-documents": "Berichtsvorlagen",
"setup": "Dieser Platzhalter wird durch die Nummer der Seite ersetzt, auf der sich die Schwärzung befindet.", "setup": "Dieser Platzhalter wird durch die Nummer der Seite ersetzt, auf der sich die Schwärzung befindet.",

View File

@ -983,7 +983,7 @@
"download-file-disabled": "To download, ensure you are an approver in the dossier, and the {count, plural, one{file has undergone} other{files have undergone}} initial processing.", "download-file-disabled": "To download, ensure you are an approver in the dossier, and the {count, plural, one{file has undergone} other{files have undergone}} initial processing.",
"file-listing": { "file-listing": {
"file-entry": { "file-entry": {
"file-error": "Re-processing required", "file-error": "Re-processing required ({errorCode, select, RULES_EXECUTION_TIMEOUT{Rules timeout} LOCKED_RULES{Rules locked} other{unknown}})",
"file-pending": "Pending..." "file-pending": "Pending..."
} }
}, },
@ -2097,6 +2097,9 @@
"processing-status": { "processing-status": {
"ocr": "OCR", "ocr": "OCR",
"pending": "Pending", "pending": "Pending",
"pending-locked-rules": "Pending (Rules locked)",
"pending-timeout": "Pending (Rules timeout)",
"pending-unknown": "Pending (unknown)",
"processed": "Processed", "processed": "Processed",
"processing": "Processing" "processing": "Processing"
}, },

View File

@ -1,22 +1,6 @@
import { import { isProcessingStatuses, OCR_STATES, PENDING_STATES, PROCESSED_STATES, PROCESSING_STATES, ProcessingFileStatus } from '../files';
isProcessingStatuses,
OCR_STATES,
PENDING_STATES,
PROCESSED_STATES,
PROCESSING_STATES,
ProcessingFileStatus,
} from '../files/types';
import { IDossierStats } from './dossier-stats'; import { IDossierStats } from './dossier-stats';
import { FileCountPerProcessingStatus, FileCountPerWorkflowStatus } from './types'; import { FileCountPerProcessingStatus, FileCountPerWorkflowStatus, ProcessingType } from './types';
export const ProcessingTypes = {
pending: 'pending',
ocr: 'ocr',
processing: 'processing',
processed: 'processed',
} as const;
export type ProcessingType = keyof typeof ProcessingTypes;
export type ProcessingStats = Record<ProcessingType, number>; export type ProcessingStats = Record<ProcessingType, number>;

View File

@ -1,4 +1,21 @@
import { ProcessingFileStatus, WorkflowFileStatus } from '../files/types'; import { ProcessingFileStatus, WorkflowFileStatus } from '../files';
export type FileCountPerWorkflowStatus = { [key in WorkflowFileStatus]?: number }; export type FileCountPerWorkflowStatus = { [key in WorkflowFileStatus]?: number };
export type FileCountPerProcessingStatus = { [key in ProcessingFileStatus]?: number }; export type FileCountPerProcessingStatus = { [key in ProcessingFileStatus]?: number };
export const ProcessingTypes = {
pending: 'pending',
ocr: 'ocr',
processing: 'processing',
processed: 'processed',
} as const;
export type ProcessingType = keyof typeof ProcessingTypes;
export const PendingTypes = {
lockedRules: 'lockedRules',
timeout: 'timeout',
unknown: 'unknown',
} as const;
export type PendingType = keyof typeof PendingTypes;

View File

@ -1,10 +1,12 @@
import { Entity } from '@iqser/common-ui'; import { Entity } from '@iqser/common-ui';
import { ProcessingType, ProcessingTypes } from '../dossier-stats/dossier-stats.model'; import { PendingType, PendingTypes, ProcessingType, ProcessingTypes } from '../dossier-stats';
import { ARCHIVE_ROUTE, DOSSIERS_ROUTE } from '../dossiers/constants'; import { ARCHIVE_ROUTE, DOSSIERS_ROUTE } from '../dossiers';
import { FileAttributes } from '../file-attributes/file-attributes'; import { FileAttributes } from '../file-attributes';
import { StatusSorter } from '../shared/sorters/status-sorter'; import { StatusSorter } from '../shared';
import { IFile } from './file'; import { IFile } from './file';
import { import {
FileErrorCode,
FileErrorCodes,
isFullProcessingStatuses, isFullProcessingStatuses,
isProcessingStatuses, isProcessingStatuses,
OCR_STATES, OCR_STATES,
@ -81,6 +83,8 @@ export class File extends Entity<IFile> implements IFile {
readonly canBeOCRed: boolean; readonly canBeOCRed: boolean;
readonly processingType: ProcessingType; readonly processingType: ProcessingType;
readonly errorCode?: FileErrorCode;
readonly pendingType?: PendingType;
constructor( constructor(
file: IFile, file: IFile,
@ -120,7 +124,7 @@ export class File extends Entity<IFile> implements IFile {
this.workflowStatus = file.workflowStatus; this.workflowStatus = file.workflowStatus;
this.isError = this.processingStatus === ProcessingFileStatuses.ERROR; this.isError = this.processingStatus === ProcessingFileStatuses.ERROR;
this.isUnprocessed = this.processingStatus === ProcessingFileStatuses.UNPROCESSED; this.isUnprocessed = this.processingStatus === ProcessingFileStatuses.UNPROCESSED;
this.numberOfPages = this.isError ? 0 : file.numberOfPages ?? 0; this.numberOfPages = this.isError ? 0 : (file.numberOfPages ?? 0);
this.rulesVersion = file.rulesVersion; this.rulesVersion = file.rulesVersion;
this.uploader = file.uploader; this.uploader = file.uploader;
this.excludedPages = file.excludedPages || []; this.excludedPages = file.excludedPages || [];
@ -157,6 +161,8 @@ export class File extends Entity<IFile> implements IFile {
file.fileAttributes && file.fileAttributes.attributeIdToValue ? file.fileAttributes : { attributeIdToValue: {} }; file.fileAttributes && file.fileAttributes.attributeIdToValue ? file.fileAttributes : { attributeIdToValue: {} };
this.processingType = this.#processingType; this.processingType = this.#processingType;
this.errorCode = file.fileErrorInfo?.errorCode;
this.pendingType = this.processingType === ProcessingTypes.pending ? this.#pendingType : undefined;
} }
get deleted(): boolean { get deleted(): boolean {
@ -189,6 +195,16 @@ export class File extends Entity<IFile> implements IFile {
return ProcessingTypes.processed; return ProcessingTypes.processed;
} }
get #pendingType(): PendingType {
if (this.errorCode === FileErrorCodes.LOCKED_RULES) {
return PendingTypes.lockedRules;
}
if (this.errorCode === FileErrorCodes.RULES_EXECUTION_TIMEOUT) {
return PendingTypes.timeout;
}
return PendingTypes.unknown;
}
isPageExcluded(page: number): boolean { isPageExcluded(page: number): boolean {
return this.excludedPages.includes(page); return this.excludedPages.includes(page);
} }

View File

@ -2,7 +2,7 @@
* Object containing information on a specific file. * Object containing information on a specific file.
*/ */
import { FileAttributes } from '../file-attributes'; import { FileAttributes } from '../file-attributes';
import { ProcessingFileStatus, WorkflowFileStatus } from './types'; import { FileErrorInfo, ProcessingFileStatus, WorkflowFileStatus } from './types';
export interface IFile { export interface IFile {
/** /**
@ -147,4 +147,5 @@ export interface IFile {
readonly fileManipulationDate: string | null; readonly fileManipulationDate: string | null;
readonly redactionModificationDate: string | null; readonly redactionModificationDate: string | null;
readonly lastManualChangeDate?: string; readonly lastManualChangeDate?: string;
readonly fileErrorInfo?: FileErrorInfo;
} }

View File

@ -96,3 +96,18 @@ export const PROCESSING_STATES: ProcessingFileStatus[] = [
export const PROCESSED_STATES: ProcessingFileStatus[] = [ProcessingFileStatuses.PROCESSED]; export const PROCESSED_STATES: ProcessingFileStatus[] = [ProcessingFileStatuses.PROCESSED];
export const OCR_STATES: ProcessingFileStatus[] = [ProcessingFileStatuses.OCR_PROCESSING, ProcessingFileStatuses.OCR_PROCESSING_QUEUED]; export const OCR_STATES: ProcessingFileStatus[] = [ProcessingFileStatuses.OCR_PROCESSING, ProcessingFileStatuses.OCR_PROCESSING_QUEUED];
export const FileErrorCodes = {
RULES_EXECUTION_TIMEOUT: 'RULES_EXECUTION_TIMEOUT',
LOCKED_RULES: 'LOCKED_RULES',
} as const;
export type FileErrorCode = keyof typeof FileErrorCodes;
export interface FileErrorInfo {
cause: string;
queue: string;
service: string;
timestamp: string;
errorCode?: FileErrorCode;
}