271 lines
12 KiB
TypeScript
271 lines
12 KiB
TypeScript
import { HttpErrorResponse } from '@angular/common/http';
|
|
import { Injectable } from '@angular/core';
|
|
import { marker as _ } from '@biesbjerg/ngx-translate-extract-marker';
|
|
import { EntitiesService, isIqserDevMode, Toaster } from '@iqser/common-ui';
|
|
import { List } from '@iqser/common-ui/lib/utils';
|
|
import { Dictionary, DictionaryEntryType, DictionaryEntryTypes, IDictionary, IUpdateDictionary, SuperTypes } from '@red/domain';
|
|
import { firstValueFrom, forkJoin, Observable } from 'rxjs';
|
|
import { map, switchMap, tap } from 'rxjs/operators';
|
|
import { IMAGE_CATEGORIES } from '../../modules/file-preview/utils/constants';
|
|
import { DictionariesMapService } from './dictionaries-map.service';
|
|
import { DossierTemplateStatsService } from './dossier-template-stats.service';
|
|
|
|
const IMAGE_TYPES = ['image', 'formula', 'ocr'];
|
|
|
|
@Injectable({
|
|
providedIn: 'root',
|
|
})
|
|
export class DictionaryService extends EntitiesService<IDictionary, Dictionary> {
|
|
protected readonly _defaultModelPath = 'dictionary';
|
|
protected readonly _entityClass = Dictionary;
|
|
readonly #isIqserDevMode = isIqserDevMode();
|
|
|
|
constructor(
|
|
private readonly _toaster: Toaster,
|
|
private readonly _dossierTemplateStatsService: DossierTemplateStatsService,
|
|
private readonly _dictionariesMapService: DictionariesMapService,
|
|
) {
|
|
super();
|
|
}
|
|
|
|
/**
|
|
* Retrieves all dictionary entries of an entry type
|
|
*/
|
|
|
|
getForType(dossierTemplateId: string, type: string, dossierId?: string) {
|
|
const queryParams = dossierId ? [{ key: 'dossierId', value: dossierId }] : undefined;
|
|
return firstValueFrom(this._getOne([type, dossierTemplateId], this._defaultModelPath, queryParams));
|
|
}
|
|
|
|
deleteDictionaries(dictionaryIds: List, dossierTemplateId: string, dossierId?: string): Observable<unknown> {
|
|
const queryParams = dossierId ? [{ key: 'dossierId', value: dossierId }] : undefined;
|
|
const url = `${this._defaultModelPath}/type/${dossierTemplateId}/delete`;
|
|
return this._post(dictionaryIds, url, queryParams).pipe(
|
|
switchMap(() => this.loadDictionaryDataForDossierTemplate(dossierTemplateId)),
|
|
switchMap(() => this._dossierTemplateStatsService.getFor([dossierTemplateId])),
|
|
);
|
|
}
|
|
|
|
getAllDictionaries(dossierTemplateId: string, readOnly = false, dossierId?: string) {
|
|
const queryParams = [];
|
|
if (dossierId) {
|
|
queryParams.push({ key: 'dossierId', value: dossierId });
|
|
}
|
|
if (readOnly) {
|
|
queryParams.push({ key: 'includeDeleted', value: true });
|
|
}
|
|
return this._getOne<{ types: IDictionary[] }>(['type', dossierTemplateId], this._defaultModelPath, queryParams).pipe(
|
|
map(typesResponse => typesResponse.types.map(type => new Dictionary(type))),
|
|
);
|
|
}
|
|
|
|
/**
|
|
* Updates colors, hint and caseInsensitive of an entry type.
|
|
*/
|
|
|
|
async update(body: IUpdateDictionary, dossierTemplateId: string, type: string, dossierId?: string): Promise<unknown> {
|
|
const url = `${this._defaultModelPath}/type/${type}/${dossierTemplateId}`;
|
|
const queryParams = dossierId ? [{ key: 'dossierId', value: dossierId }] : undefined;
|
|
const request = this._post(body, url, queryParams).pipe(
|
|
switchMap(() => this.loadDictionaryDataForDossierTemplate(dossierTemplateId)),
|
|
switchMap(() => this._dossierTemplateStatsService.getFor([dossierTemplateId])),
|
|
);
|
|
|
|
return await firstValueFrom(request);
|
|
}
|
|
|
|
async updateFlag(dossierTemplateId: string, type: string, dossierId: string, addToDictionary: boolean): Promise<unknown> {
|
|
const url = `${this._defaultModelPath}/updateFlag/${type}/${dossierTemplateId}/${dossierId}`;
|
|
const queryParams = [{ key: 'addToDictionary', value: addToDictionary }];
|
|
const request = this._post(null, url, queryParams).pipe(
|
|
switchMap(() => this.loadDictionaryDataForDossier(dossierTemplateId, dossierId)),
|
|
);
|
|
|
|
return await firstValueFrom(request);
|
|
}
|
|
|
|
async add(dictionary: IDictionary, dossierId?: string): Promise<unknown> {
|
|
const queryParams = dossierId ? [{ key: 'dossierId', value: dossierId }] : undefined;
|
|
const request = this._post(dictionary, `${this._defaultModelPath}/type`, queryParams).pipe(
|
|
switchMap(() => this.loadDictionaryDataForDossierTemplate(dictionary.dossierTemplateId)),
|
|
switchMap(() => this._dossierTemplateStatsService.getFor([dictionary.dossierTemplateId])),
|
|
);
|
|
|
|
return await firstValueFrom(request);
|
|
}
|
|
|
|
async saveEntries(
|
|
entries: List,
|
|
initialEntries: List,
|
|
dossierTemplateId: string,
|
|
type: string,
|
|
dossierId: string,
|
|
showToast = true,
|
|
dictionaryEntryType = DictionaryEntryTypes.ENTRY,
|
|
) {
|
|
const entriesToAdd: Array<string> = [];
|
|
const initialEntriesSet = new Set(initialEntries);
|
|
for (let i = 0; i < entries.length; i++) {
|
|
const entry = entries.at(i);
|
|
if (!entry.trim() || initialEntriesSet.has(entry)) {
|
|
continue;
|
|
}
|
|
entriesToAdd.push(entry);
|
|
}
|
|
const entriesToDelete: Array<string> = [];
|
|
const entriesSet = new Set(entries);
|
|
for (let i = 0; i < initialEntries.length; i++) {
|
|
const entry = initialEntries.at(i);
|
|
if (entriesSet.has(entry)) {
|
|
continue;
|
|
}
|
|
entriesToDelete.push(entry);
|
|
}
|
|
|
|
try {
|
|
await this.#updateEntries(entriesToAdd, entriesToDelete, dossierTemplateId, type, dictionaryEntryType, dossierId);
|
|
|
|
if (showToast) {
|
|
this._toaster.success(_('dictionary-overview.success.generic'));
|
|
}
|
|
} catch (error) {
|
|
if ((error as HttpErrorResponse).status === 400) {
|
|
this._toaster.error(_('dictionary-overview.error.400'));
|
|
} else {
|
|
this._toaster.error(_('dictionary-overview.error.generic'));
|
|
}
|
|
throw error;
|
|
}
|
|
}
|
|
|
|
hasManualType(dossierTemplateId: string): boolean {
|
|
return !!this._dictionariesMapService.get(dossierTemplateId).find(e => e.type === SuperTypes.ManualRedaction && !e.virtual);
|
|
}
|
|
|
|
hasType(dossierTemplateId: string, type: string): boolean {
|
|
return !!this._dictionariesMapService.get(dossierTemplateId).find(e => e.type === type && !e.virtual);
|
|
}
|
|
|
|
getDictionaries(dossierTemplateId: string) {
|
|
return this._dictionariesMapService
|
|
.get(dossierTemplateId)
|
|
.filter(d => d.model['typeId'] && (d.hasDictionary || d.addToDictionaryAction));
|
|
}
|
|
|
|
getRedactTextDictionaries(dossierId: string, dossierDictionaryOnly: boolean, dossierTemplateId: string): Dictionary[] {
|
|
const types = dossierDictionaryOnly ? this.#extractDossierLevelTypes(dossierId) : this.getDictionariesOptions(dossierTemplateId);
|
|
return types
|
|
.filter(d => d.model['typeId'] && !d.hint && d.addToDictionaryAction && (dossierDictionaryOnly || !d.dossierDictionaryOnly))
|
|
.sort((a, b) => a.label.localeCompare(b.label));
|
|
}
|
|
|
|
getEditableRedactionTypes(
|
|
dossierId: string,
|
|
isImage: boolean,
|
|
isHint: boolean,
|
|
isOCR: boolean,
|
|
currentlySelectedType: string,
|
|
): Dictionary[] {
|
|
return this.#extractDossierLevelTypes(dossierId)
|
|
.filter(
|
|
d =>
|
|
d.model['typeId'] &&
|
|
(isImage
|
|
? (isOCR ? [...IMAGE_CATEGORIES, 'ocr'] : IMAGE_CATEGORIES).includes(d.type)
|
|
: (isHint ? d.hint : !d.hint) &&
|
|
(d.addToDictionaryAction || currentlySelectedType === d.type) &&
|
|
!d.virtual &&
|
|
!d.systemManaged &&
|
|
![...IMAGE_CATEGORIES, 'ocr'].includes(d.type)),
|
|
)
|
|
.sort((a, b) => a.label.localeCompare(b.label));
|
|
}
|
|
|
|
getAddHintDictionaries(dossierId: string, dossierDictionaryOnly: boolean, dictionaryRequest: boolean): Dictionary[] {
|
|
const dictionaries: Dictionary[] = [];
|
|
this.#extractDossierLevelTypes(dossierId).forEach((d: Dictionary) => {
|
|
if (d.hint) {
|
|
if (dictionaryRequest) {
|
|
if (d.hasDictionary && d.addToDictionaryAction) {
|
|
if (d.dossierDictionaryOnly) {
|
|
if (dossierDictionaryOnly) {
|
|
dictionaries.push(d);
|
|
}
|
|
} else {
|
|
dictionaries.push(d);
|
|
}
|
|
}
|
|
} else if (!IMAGE_TYPES.includes(d.type)) {
|
|
dictionaries.push(d);
|
|
}
|
|
}
|
|
});
|
|
|
|
return dictionaries.sort((a, b) => a.label.localeCompare(b.label));
|
|
}
|
|
|
|
getRedactionTypes(dossierTemplateId: string): Dictionary[] {
|
|
return this._dictionariesMapService
|
|
.get(dossierTemplateId)
|
|
.filter(dictionary => !dictionary.virtual && !dictionary.hint && !dictionary.systemManaged)
|
|
.sort((a, b) => a.label.localeCompare(b.label));
|
|
}
|
|
|
|
getDictionariesOptions(dossierTemplateId: string): Dictionary[] {
|
|
return this._dictionariesMapService
|
|
.get(dossierTemplateId)
|
|
.filter(dictionary => !dictionary.virtual && dictionary.addToDictionaryAction)
|
|
.sort((a, b) => a.label.localeCompare(b.label));
|
|
}
|
|
|
|
loadDictionaryDataForDossierTemplate(dossierTemplateId: string): Observable<Dictionary[]> {
|
|
return this.getAllDictionaries(dossierTemplateId).pipe(
|
|
tap(dictionaries => this._dictionariesMapService.set(dossierTemplateId, dictionaries)),
|
|
);
|
|
}
|
|
|
|
loadDictionaryDataForDossier(dossierTemplateId: string, dossierId: string): Observable<Dictionary[]> {
|
|
return this.getAllDictionaries(dossierTemplateId, false, dossierId).pipe(
|
|
tap(dictionaries => this._dictionariesMapService.set(dossierId, dictionaries)),
|
|
);
|
|
}
|
|
|
|
loadTemporaryDictionaryData(dossierTemplateId: string, readOnlyFile = true): Observable<Dictionary[]> {
|
|
return this.getAllDictionaries(dossierTemplateId, readOnlyFile);
|
|
}
|
|
|
|
loadDictionaryEntriesByType(types: string[], dossierTemplateId: string, dossierId: string): Observable<Dictionary[]> {
|
|
const queryParams = [{ key: 'dossierId', value: dossierId }];
|
|
const requests = [];
|
|
for (const type of types) {
|
|
const request = this.getAll(`${this._defaultModelPath}/merged/${type}/${dossierTemplateId}`, queryParams);
|
|
requests.push(request);
|
|
}
|
|
|
|
return forkJoin(requests);
|
|
}
|
|
|
|
#updateEntries(
|
|
entriesToAdd: List,
|
|
entriesToDelete: List,
|
|
dossierTemplateId: string,
|
|
type: string,
|
|
dictionaryEntryType: DictionaryEntryType,
|
|
dossierId: string,
|
|
) {
|
|
const queryParams = [
|
|
{ key: 'dossierId', value: dossierId },
|
|
{ key: 'dictionaryEntryType', value: dictionaryEntryType },
|
|
];
|
|
const url = `${this._defaultModelPath}/update/${type}/${dossierTemplateId}`;
|
|
return firstValueFrom(this._post({ entriesToAdd, entriesToDelete }, url, queryParams));
|
|
}
|
|
|
|
#extractDossierLevelTypes(dossierId: string) {
|
|
return this._dictionariesMapService
|
|
.get(dossierId)
|
|
.filter(dictionary => dictionary.model['typeId']?.includes(dossierId))
|
|
.filter(dictionary => !(dictionary.experimental && !this.#isIqserDevMode));
|
|
}
|
|
}
|