False positive & recommendations
This commit is contained in:
parent
e5686441cf
commit
52681330f5
@ -1,5 +1,5 @@
|
||||
import { AnnotationWrapper } from './annotation.wrapper';
|
||||
import { User } from '@red/domain';
|
||||
import { Dictionary, User } from '@red/domain';
|
||||
import { isArray } from 'lodash-es';
|
||||
|
||||
export class AnnotationPermissions {
|
||||
@ -16,7 +16,7 @@ export class AnnotationPermissions {
|
||||
canRecategorizeImage = true;
|
||||
canForceHint = true;
|
||||
|
||||
static forUser(isApprover: boolean, user: User, annotations: AnnotationWrapper | AnnotationWrapper[]) {
|
||||
static forUser(isApprover: boolean, user: User, annotations: AnnotationWrapper | AnnotationWrapper[], entities: Dictionary[]) {
|
||||
if (!isArray(annotations)) {
|
||||
annotations = [annotations];
|
||||
}
|
||||
@ -35,7 +35,8 @@ export class AnnotationPermissions {
|
||||
permissions.canForceRedaction = annotation.isSkipped && !annotation.isFalsePositive && !annotation.pending;
|
||||
permissions.canAcceptRecommendation = annotation.isRecommendation && !annotation.pending;
|
||||
|
||||
permissions.canMarkAsFalsePositive = annotation.canBeMarkedAsFalsePositive && !annotation.imported && !annotation.pending;
|
||||
const annotationEntity = entities.find(entity => entity.type === annotation.type);
|
||||
permissions.canMarkAsFalsePositive = annotation.canBeMarkedAsFalsePositive && annotationEntity.hasDictionary;
|
||||
|
||||
permissions.canRemoveOrSuggestToRemoveOnlyHere = (annotation.isRedacted || annotation.isHint) && !annotation.pending;
|
||||
permissions.canRemoveOrSuggestToRemoveFromDictionary =
|
||||
|
||||
@ -79,7 +79,7 @@ export class AnnotationWrapper implements Record<string, unknown> {
|
||||
}
|
||||
|
||||
get canBeMarkedAsFalsePositive() {
|
||||
return (this.isRecommendation || this.superType === SuperTypes.Redaction) && !this.isImage;
|
||||
return (this.isRecommendation || this.superType === SuperTypes.Redaction) && !this.isImage && !this.imported && !this.pending;
|
||||
}
|
||||
|
||||
get isSuperTypeBasedColor() {
|
||||
@ -294,7 +294,7 @@ export class AnnotationWrapper implements Record<string, unknown> {
|
||||
|
||||
private static _handleRecommendations(annotationWrapper: AnnotationWrapper, redactionLogEntry: RedactionLogEntry) {
|
||||
if (annotationWrapper.superType === SuperTypes.Recommendation) {
|
||||
annotationWrapper.recommendationType = redactionLogEntry.type.substr('recommendation_'.length);
|
||||
annotationWrapper.recommendationType = redactionLogEntry.type;
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@ -19,6 +19,16 @@ const routes = [
|
||||
component: DictionaryScreenComponent,
|
||||
canDeactivate: [PendingChangesGuard],
|
||||
},
|
||||
{
|
||||
path: 'false-positive',
|
||||
component: DictionaryScreenComponent,
|
||||
canDeactivate: [PendingChangesGuard],
|
||||
},
|
||||
{
|
||||
path: 'false-recommendations',
|
||||
component: DictionaryScreenComponent,
|
||||
canDeactivate: [PendingChangesGuard],
|
||||
},
|
||||
];
|
||||
|
||||
@NgModule({
|
||||
|
||||
@ -6,6 +6,7 @@ import { LoadingService } from '@iqser/common-ui';
|
||||
import { UserService } from '@services/user.service';
|
||||
import { BehaviorSubject, firstValueFrom } from 'rxjs';
|
||||
import { DOSSIER_TEMPLATE_ID, ENTITY_TYPE } from '@utils/constants';
|
||||
import { DICTIONARY_TO_ENTRY_TYPE_MAP, DICTIONARY_TYPE_KEY_MAP, DictionaryType } from '@red/domain';
|
||||
|
||||
@Component({
|
||||
templateUrl: './dictionary-screen.component.html',
|
||||
@ -17,6 +18,7 @@ export class DictionaryScreenComponent implements OnInit {
|
||||
isLeavingPage = false;
|
||||
readonly #dossierTemplateId: string;
|
||||
readonly #entityType: string;
|
||||
readonly #type: DictionaryType;
|
||||
@ViewChild('dictionaryManager', { static: false })
|
||||
private readonly _dictionaryManager: DictionaryManagerComponent;
|
||||
@ViewChild('fileInput') private readonly _fileInput: ElementRef;
|
||||
@ -29,6 +31,7 @@ export class DictionaryScreenComponent implements OnInit {
|
||||
) {
|
||||
this.#dossierTemplateId = _route.parent.snapshot.paramMap.get(DOSSIER_TEMPLATE_ID);
|
||||
this.#entityType = _route.parent.snapshot.paramMap.get(ENTITY_TYPE);
|
||||
this.#type = this._route.snapshot.routeConfig.path as DictionaryType;
|
||||
}
|
||||
|
||||
get changed() {
|
||||
@ -45,7 +48,15 @@ export class DictionaryScreenComponent implements OnInit {
|
||||
this._loadingService.start();
|
||||
try {
|
||||
await firstValueFrom(
|
||||
this._dictionaryService.saveEntries(entries, this.initialEntries$.value, this.#dossierTemplateId, this.#entityType, null),
|
||||
this._dictionaryService.saveEntries(
|
||||
entries,
|
||||
this.initialEntries$.value,
|
||||
this.#dossierTemplateId,
|
||||
this.#entityType,
|
||||
null,
|
||||
true,
|
||||
DICTIONARY_TO_ENTRY_TYPE_MAP[this.#type],
|
||||
),
|
||||
);
|
||||
await this._loadEntries();
|
||||
} catch (e) {
|
||||
@ -57,9 +68,8 @@ export class DictionaryScreenComponent implements OnInit {
|
||||
this._loadingService.start();
|
||||
try {
|
||||
const data = await firstValueFrom(this._dictionaryService.getForType(this.#dossierTemplateId, this.#entityType));
|
||||
this.initialEntries$.next(
|
||||
[...data.entries].sort((str1, str2) => str1.localeCompare(str2, undefined, { sensitivity: 'accent' })),
|
||||
);
|
||||
const entries: string[] = data[DICTIONARY_TYPE_KEY_MAP[this.#type]];
|
||||
this.initialEntries$.next([...entries].sort((str1, str2) => str1.localeCompare(str2, undefined, { sensitivity: 'accent' })));
|
||||
this._loadingService.stop();
|
||||
} catch (e) {
|
||||
this._loadingService.stop();
|
||||
|
||||
@ -72,7 +72,7 @@
|
||||
|
||||
<div class="iqser-input-group mb-14">
|
||||
<label translate="entity.info.form.technical-name"></label>
|
||||
<div class="technical-name">{{ dictionary.type }}</div>
|
||||
<div class="technical-name">{{ entity.type }}</div>
|
||||
<span class="hint" translate="entity.info.form.technical-name-hint"></span>
|
||||
</div>
|
||||
|
||||
|
||||
@ -18,7 +18,7 @@ import { DictionaryService } from '@services/entity-services/dictionary.service'
|
||||
changeDetection: ChangeDetectionStrategy.OnPush,
|
||||
})
|
||||
export class InfoComponent extends BaseFormComponent {
|
||||
dictionary: Dictionary;
|
||||
entity: Dictionary;
|
||||
readonly hasHexColor$: Observable<boolean>;
|
||||
readonly hasRecommendationHexColor$: Observable<boolean>;
|
||||
readonly currentUser = this._userService.currentUser;
|
||||
@ -37,7 +37,7 @@ export class InfoComponent extends BaseFormComponent {
|
||||
super();
|
||||
this.#dossierTemplateId = this._route.parent.snapshot.paramMap.get(DOSSIER_TEMPLATE_ID);
|
||||
const entityType = this._route.parent.snapshot.paramMap.get(ENTITY_TYPE);
|
||||
this.dictionary = this._dictionariesMapService.getDictionary(entityType, this.#dossierTemplateId);
|
||||
this.entity = this._dictionariesMapService.getDictionary(entityType, this.#dossierTemplateId);
|
||||
this.form = this._getForm();
|
||||
this.initialFormValue = this.form.getRawValue();
|
||||
this.hasHexColor$ = this._colorEmpty$('hexColor');
|
||||
@ -71,7 +71,7 @@ export class InfoComponent extends BaseFormComponent {
|
||||
|
||||
private _formToObject(): IDictionary {
|
||||
return {
|
||||
type: this.dictionary.type,
|
||||
type: this.entity.type,
|
||||
label: this.form.get('label').value,
|
||||
caseInsensitive: !this.form.get('caseSensitive').value,
|
||||
description: this.form.get('description').value,
|
||||
@ -81,21 +81,22 @@ export class InfoComponent extends BaseFormComponent {
|
||||
rank: this.form.get('rank').value,
|
||||
addToDictionaryAction: this.form.get('addToDictionaryAction').value,
|
||||
dossierTemplateId: this.#dossierTemplateId,
|
||||
hasDictionary: this.form.get('hasDictionary').value,
|
||||
};
|
||||
}
|
||||
|
||||
private _getForm(): FormGroup {
|
||||
return this._formBuilder.group({
|
||||
label: [{ value: this.dictionary.label, disabled: !this.currentUser.isAdmin }, [Validators.required, Validators.minLength(3)]],
|
||||
description: [this.dictionary.description],
|
||||
rank: [this.dictionary.rank, Validators.required],
|
||||
hexColor: [this.dictionary.hexColor, [Validators.required, Validators.minLength(7)]],
|
||||
recommendationHexColor: [this.dictionary.recommendationHexColor, [Validators.required, Validators.minLength(7)]],
|
||||
hint: [this.dictionary.hint],
|
||||
addToDictionaryAction: [this.dictionary.addToDictionaryAction],
|
||||
caseSensitive: [!this.dictionary.caseInsensitive],
|
||||
label: [{ value: this.entity.label, disabled: !this.currentUser.isAdmin }, [Validators.required, Validators.minLength(3)]],
|
||||
description: [this.entity.description],
|
||||
rank: [this.entity.rank, Validators.required],
|
||||
hexColor: [this.entity.hexColor, [Validators.required, Validators.minLength(7)]],
|
||||
recommendationHexColor: [this.entity.recommendationHexColor, [Validators.required, Validators.minLength(7)]],
|
||||
hint: [this.entity.hint],
|
||||
addToDictionaryAction: [this.entity.addToDictionaryAction],
|
||||
caseSensitive: [!this.entity.caseInsensitive],
|
||||
defaultReason: [{ value: null, disabled: true }],
|
||||
hasDictionary: [false],
|
||||
hasDictionary: [this.entity.hasDictionary],
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
@ -28,7 +28,3 @@
|
||||
iqser-status-bar {
|
||||
margin-left: 2px;
|
||||
}
|
||||
|
||||
.spinning-icon {
|
||||
margin: 0 12px 0 11px;
|
||||
}
|
||||
|
||||
@ -10,6 +10,7 @@ import { FilePreviewStateService } from '../../services/file-preview-state.servi
|
||||
import { HelpModeService, ScrollableParentView, ScrollableParentViews } from '@iqser/common-ui';
|
||||
import { PdfViewer } from '../../services/pdf-viewer.service';
|
||||
import { FileDataService } from '../../services/file-data.service';
|
||||
import { DictionariesMapService } from '@services/entity-services/dictionaries-map.service';
|
||||
|
||||
export const AnnotationButtonTypes = {
|
||||
dark: 'dark',
|
||||
@ -41,6 +42,7 @@ export class AnnotationActionsComponent implements OnChanges {
|
||||
private readonly _state: FilePreviewStateService,
|
||||
private readonly _permissionsService: PermissionsService,
|
||||
private readonly _fileDataService: FileDataService,
|
||||
private readonly _dictionariesMapService: DictionariesMapService,
|
||||
) {}
|
||||
|
||||
private _annotations: AnnotationWrapper[];
|
||||
@ -131,6 +133,7 @@ export class AnnotationActionsComponent implements OnChanges {
|
||||
this._permissionsService.isApprover(dossier),
|
||||
this._userService.currentUser,
|
||||
this.annotations,
|
||||
this._dictionariesMapService.get(dossier.dossierTemplateId),
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
@ -30,10 +30,9 @@ export class TypeAnnotationIconComponent implements OnChanges {
|
||||
|
||||
if (isHighlight) {
|
||||
this.color = this.annotation.color;
|
||||
} else if (this.annotation.isSuperTypeBasedColor) {
|
||||
this.color = this._dictionariesMapService.getDictionaryColor(this.annotation.superType, this._dossierTemplateId);
|
||||
} else {
|
||||
this.color = this._dictionariesMapService.getDictionaryColor(this.annotation.type, this._dossierTemplateId);
|
||||
const type = this.annotation.isSuperTypeBasedColor ? this.annotation.superType : this.annotation.type;
|
||||
this.color = this._dictionariesMapService.getDictionaryColor(type, this._dossierTemplateId, this.annotation.isRecommendation);
|
||||
}
|
||||
|
||||
this.type =
|
||||
|
||||
@ -9,7 +9,7 @@ import { AnnotationPermissions } from '@models/file/annotation.permissions';
|
||||
import { BASE_HREF } from '../../../tokens';
|
||||
import { UserService } from '@services/user.service';
|
||||
import { Core } from '@pdftron/webviewer';
|
||||
import { Dossier, IAddRedactionRequest, ILegalBasisChangeRequest, IRectangle, IResizeRequest } from '@red/domain';
|
||||
import { DictionaryEntryTypes, Dossier, IAddRedactionRequest, ILegalBasisChangeRequest, IRectangle, IResizeRequest } from '@red/domain';
|
||||
import { toPosition } from '../../dossier/utils/pdf-calculation.utils';
|
||||
import { AnnotationDrawService } from './annotation-draw.service';
|
||||
import { ActiveDossiersService } from '@services/dossiers/active-dossiers.service';
|
||||
@ -24,6 +24,7 @@ import { MatDialog } from '@angular/material/dialog';
|
||||
import { FilePreviewStateService } from './file-preview-state.service';
|
||||
import { PdfViewer } from './pdf-viewer.service';
|
||||
import { FilePreviewDialogService } from './file-preview-dialog.service';
|
||||
import { DictionariesMapService } from '@services/entity-services/dictionaries-map.service';
|
||||
import Quad = Core.Math.Quad;
|
||||
|
||||
@Injectable()
|
||||
@ -41,6 +42,7 @@ export class AnnotationActionsService {
|
||||
private readonly _annotationDrawService: AnnotationDrawService,
|
||||
private readonly _activeDossiersService: ActiveDossiersService,
|
||||
private readonly _screenStateService: FilePreviewStateService,
|
||||
private readonly _dictionariesMapService: DictionariesMapService,
|
||||
) {}
|
||||
|
||||
private get _dossier(): Dossier {
|
||||
@ -223,6 +225,7 @@ export class AnnotationActionsService {
|
||||
this._permissionsService.isApprover(dossier),
|
||||
this._userService.currentUser,
|
||||
annotation,
|
||||
this._dictionariesMapService.get(dossier.dossierTemplateId),
|
||||
),
|
||||
}));
|
||||
|
||||
@ -500,13 +503,17 @@ export class AnnotationActionsService {
|
||||
) {
|
||||
$event?.stopPropagation();
|
||||
|
||||
const falsePositiveRequest: IAddRedactionRequest = {};
|
||||
falsePositiveRequest.reason = annotation.id;
|
||||
falsePositiveRequest.value = text;
|
||||
falsePositiveRequest.type = 'false_positive';
|
||||
falsePositiveRequest.positions = annotation.positions;
|
||||
falsePositiveRequest.addToDictionary = true;
|
||||
falsePositiveRequest.comment = { text: 'False Positive' };
|
||||
const falsePositiveRequest: IAddRedactionRequest = {
|
||||
reason: annotation.id,
|
||||
value: text,
|
||||
type: annotation.type,
|
||||
positions: annotation.positions,
|
||||
addToDictionary: true,
|
||||
comment: { text: 'False Positive' },
|
||||
dictionaryEntryType: annotation.isRecommendation
|
||||
? DictionaryEntryTypes.FALSE_RECOMMENDATION
|
||||
: DictionaryEntryTypes.FALSE_POSITIVE,
|
||||
};
|
||||
const { dossierId, fileId } = this._screenStateService;
|
||||
|
||||
this._processObsAndEmit(
|
||||
|
||||
@ -46,9 +46,11 @@ export class AnnotationDrawService {
|
||||
switch (superType) {
|
||||
case SuperTypes.Hint:
|
||||
case SuperTypes.Redaction:
|
||||
case SuperTypes.Recommendation:
|
||||
color = this._dictionariesMapService.getDictionaryColor(dictionary, this._state.dossierTemplateId);
|
||||
break;
|
||||
case SuperTypes.Recommendation:
|
||||
color = this._dictionariesMapService.getDictionaryColor(dictionary, this._state.dossierTemplateId, true);
|
||||
break;
|
||||
case SuperTypes.Skipped:
|
||||
color = this._dictionariesMapService.getDictionaryColor(superType, this._state.dossierTemplateId);
|
||||
break;
|
||||
|
||||
@ -42,7 +42,7 @@
|
||||
</div>
|
||||
|
||||
<div class="iqser-input-group w-200 mt-0">
|
||||
<mat-select [(ngModel)]="dictionary" [disabled]="!compare || dossierTemplateIsNotSelected">
|
||||
<mat-select [(ngModel)]="compareDictionary" [disabled]="!compare || dossierTemplateIsNotSelected">
|
||||
<mat-option [value]="selectDictionary">{{ selectDictionary.label | translate }}</mat-option>
|
||||
<mat-option *ngFor="let dictionary of dictionaries" [value]="dictionary">
|
||||
{{ dictionary.label }}
|
||||
|
||||
@ -1,5 +1,5 @@
|
||||
import { Component, EventEmitter, Input, OnChanges, Output, SimpleChanges, ViewChild } from '@angular/core';
|
||||
import { Debounce, IconButtonTypes, List } from '@iqser/common-ui';
|
||||
import { ChangeDetectorRef, Component, EventEmitter, Input, OnChanges, Output, SimpleChanges, ViewChild } from '@angular/core';
|
||||
import { Debounce, IconButtonTypes, List, LoadingService } from '@iqser/common-ui';
|
||||
import { firstValueFrom, Observable, of } from 'rxjs';
|
||||
import { catchError, map, take, tap } from 'rxjs/operators';
|
||||
import { Dictionary, Dossier, DossierTemplate } from '@red/domain';
|
||||
@ -47,6 +47,8 @@ export class DictionaryManagerComponent implements OnChanges {
|
||||
constructor(
|
||||
private readonly _dictionaryService: DictionaryService,
|
||||
private readonly _dictionariesMapService: DictionariesMapService,
|
||||
private readonly _loadingService: LoadingService,
|
||||
private readonly _changeRef: ChangeDetectorRef,
|
||||
readonly activeDossiersService: ActiveDossiersService,
|
||||
readonly dossierTemplatesService: DossierTemplatesService,
|
||||
) {}
|
||||
@ -60,7 +62,7 @@ export class DictionaryManagerComponent implements OnChanges {
|
||||
set dossierTemplate(value) {
|
||||
this._dossierTemplate = value;
|
||||
this.dictionaries = this._dictionaries;
|
||||
this._dictionary = this.selectDictionary;
|
||||
this._compareDictionary = this.selectDictionary;
|
||||
this.showDiffEditor = false;
|
||||
}
|
||||
|
||||
@ -87,14 +89,14 @@ export class DictionaryManagerComponent implements OnChanges {
|
||||
});
|
||||
}
|
||||
|
||||
private _dictionary = this.selectDictionary;
|
||||
private _compareDictionary = this.selectDictionary;
|
||||
|
||||
get dictionary() {
|
||||
return this._dictionary;
|
||||
get compareDictionary() {
|
||||
return this._compareDictionary;
|
||||
}
|
||||
|
||||
set dictionary(dictionary: Dictionary) {
|
||||
this._dictionary = dictionary;
|
||||
set compareDictionary(dictionary: Dictionary) {
|
||||
this._compareDictionary = dictionary;
|
||||
|
||||
if (dictionary.label === this.selectDictionary.label) {
|
||||
this.showDiffEditor = false;
|
||||
@ -102,7 +104,8 @@ export class DictionaryManagerComponent implements OnChanges {
|
||||
return;
|
||||
}
|
||||
const entries: List =
|
||||
this._dictionary.entries ?? this._dictionariesMapService.get(this._dictionary.dossierTemplateId, this._dictionary.type).entries;
|
||||
this._compareDictionary.entries ??
|
||||
this._dictionariesMapService.get(this._compareDictionary.dossierTemplateId, this._compareDictionary.type).entries;
|
||||
|
||||
if (entries.length) {
|
||||
this.diffEditorText = this._toString([...entries]);
|
||||
@ -110,17 +113,20 @@ export class DictionaryManagerComponent implements OnChanges {
|
||||
return;
|
||||
}
|
||||
|
||||
this._loadingService.start();
|
||||
firstValueFrom(
|
||||
this._dictionaryService.getForType(this._dictionary.dossierTemplateId, this._dictionary.type).pipe(
|
||||
tap(values => (this._dictionary.entries = [...values.entries] ?? [])),
|
||||
this._dictionaryService.getForType(this._compareDictionary.dossierTemplateId, this._compareDictionary.type).pipe(
|
||||
tap(values => (this._compareDictionary.entries = [...values.entries] ?? [])),
|
||||
catchError(() => {
|
||||
this._dictionary.entries = [];
|
||||
this._compareDictionary.entries = [];
|
||||
return of({});
|
||||
}),
|
||||
),
|
||||
).then(() => {
|
||||
this.diffEditorText = this._toString([...this._dictionary.entries]);
|
||||
this.diffEditorText = this._toString([...this._compareDictionary.entries]);
|
||||
this.showDiffEditor = true;
|
||||
this._changeRef.markForCheck();
|
||||
this._loadingService.stop();
|
||||
});
|
||||
}
|
||||
|
||||
@ -137,7 +143,7 @@ export class DictionaryManagerComponent implements OnChanges {
|
||||
|
||||
get optionNotSelected() {
|
||||
if (this.filterByDossierTemplate) {
|
||||
return this.selectDictionary.label === this._dictionary.label;
|
||||
return this.selectDictionary.label === this._compareDictionary.label;
|
||||
}
|
||||
return this.dossier.dossierName === this.selectDossier.dossierName;
|
||||
}
|
||||
|
||||
@ -14,7 +14,18 @@ export class DictionariesMapService extends EntitiesMapService<Dictionary, IDict
|
||||
return this.get(dossierTemplateId, type) || this.get(dossierTemplateId, 'default');
|
||||
}
|
||||
|
||||
getDictionaryColor(type: string, dossierTemplateId: string) {
|
||||
return !this.get(dossierTemplateId) ? '#cccccc' : this.getDictionary(type, dossierTemplateId)?.hexColor || '#cccccc';
|
||||
getDictionaryColor(type: string, dossierTemplateId: string, isRecommendation = false) {
|
||||
const defaultColor = '#CCCCCC';
|
||||
if (!this.get(dossierTemplateId)) {
|
||||
return defaultColor;
|
||||
}
|
||||
|
||||
const dictionary = this.getDictionary(type, dossierTemplateId);
|
||||
const colorKey = isRecommendation ? 'recommendationHexColor' : 'hexColor';
|
||||
if (dictionary && dictionary[colorKey]) {
|
||||
return dictionary[colorKey];
|
||||
}
|
||||
|
||||
return defaultColor;
|
||||
}
|
||||
}
|
||||
|
||||
@ -1,7 +1,7 @@
|
||||
import { Injectable, Injector } from '@angular/core';
|
||||
import { firstValueFrom, forkJoin, Observable, of, throwError } from 'rxjs';
|
||||
import { EntitiesService, List, QueryParam, RequiredParam, Toaster, Validate } from '@iqser/common-ui';
|
||||
import { Dictionary, IColors, IDictionary, IUpdateDictionary } from '@red/domain';
|
||||
import { Dictionary, DictionaryEntryType, DictionaryEntryTypes, IColors, IDictionary, IUpdateDictionary } from '@red/domain';
|
||||
import { catchError, map, mapTo, switchMap, tap } from 'rxjs/operators';
|
||||
import { marker as _ } from '@biesbjerg/ngx-translate-extract-marker';
|
||||
import { DossierTemplateStatsService } from './dossier-template-stats.service';
|
||||
@ -144,6 +144,7 @@ export class DictionaryService extends EntitiesService<Dictionary, IDictionary>
|
||||
type: string,
|
||||
dossierId: string,
|
||||
showToast = true,
|
||||
dictionaryEntryType = DictionaryEntryTypes.ENTRY,
|
||||
): Observable<unknown> {
|
||||
let entriesToAdd = [];
|
||||
entries.forEach(currentEntry => {
|
||||
@ -156,13 +157,13 @@ export class DictionaryService extends EntitiesService<Dictionary, IDictionary>
|
||||
// can add at least 1 - block UI
|
||||
let obs: Observable<IDictionary>;
|
||||
if (entriesToAdd.length > 0) {
|
||||
obs = this._addEntry(entriesToAdd, dossierTemplateId, type, dossierId, true);
|
||||
obs = this._addEntries(entriesToAdd, dossierTemplateId, type, dictionaryEntryType, dossierId);
|
||||
} else {
|
||||
obs = this._deleteEntries(initialEntries, dossierTemplateId, type, dossierId);
|
||||
obs = this._deleteEntries(initialEntries, dossierTemplateId, type, dictionaryEntryType, dossierId);
|
||||
}
|
||||
|
||||
return obs.pipe(
|
||||
switchMap(dictionary => this._dossierTemplateStatsService.getFor([dossierTemplateId]).pipe(mapTo(dictionary))),
|
||||
switchMap(dictionary => this._dossierTemplateStatsService.getFor([dossierTemplateId]).pipe(map(() => dictionary))),
|
||||
tap(
|
||||
() => {
|
||||
if (showToast) {
|
||||
@ -216,7 +217,7 @@ export class DictionaryService extends EntitiesService<Dictionary, IDictionary>
|
||||
const virtualTypes$: Observable<Dictionary[]> = this.getColors(dossierTemplateId).pipe(
|
||||
tap(colors => {
|
||||
for (const key of Object.keys(colors)) {
|
||||
const color = colors[key];
|
||||
const color: string = colors[key];
|
||||
try {
|
||||
const rgbValue = hexToRgb(color);
|
||||
if (!rgbValue) {
|
||||
@ -286,19 +287,20 @@ export class DictionaryService extends EntitiesService<Dictionary, IDictionary>
|
||||
* Add dictionary entries with entry type.
|
||||
*/
|
||||
@Validate()
|
||||
private _addEntry(
|
||||
@RequiredParam() body: List,
|
||||
@RequiredParam() dossierTemplateId: string,
|
||||
@RequiredParam() type: string,
|
||||
dossierId?: string,
|
||||
removeCurrent?: boolean,
|
||||
private _addEntries(
|
||||
entries: List,
|
||||
dossierTemplateId: string,
|
||||
type: string,
|
||||
dictionaryEntryType: DictionaryEntryType,
|
||||
dossierId: string,
|
||||
) {
|
||||
const queryParams: List<QueryParam> = [
|
||||
{ key: 'dossierId', value: dossierId },
|
||||
{ key: 'removeCurrent', value: removeCurrent },
|
||||
{ key: 'dictionaryEntryType', value: dictionaryEntryType },
|
||||
{ key: 'removeCurrent', value: true },
|
||||
];
|
||||
const url = `${this._defaultModelPath}/${type}/${dossierTemplateId}`;
|
||||
return this._post(body, url, queryParams);
|
||||
return this._post(entries, url, queryParams);
|
||||
}
|
||||
|
||||
/**
|
||||
@ -306,13 +308,16 @@ export class DictionaryService extends EntitiesService<Dictionary, IDictionary>
|
||||
*/
|
||||
@Validate()
|
||||
private _deleteEntries(
|
||||
@RequiredParam() body: List,
|
||||
@RequiredParam() dossierTemplateId: string,
|
||||
@RequiredParam() type: string,
|
||||
@RequiredParam() dossierId?: string,
|
||||
entries: List,
|
||||
dossierTemplateId: string,
|
||||
type: string,
|
||||
dictionaryEntryType: DictionaryEntryType,
|
||||
dossierId: string,
|
||||
) {
|
||||
const queryParams = dossierId ? [{ key: 'dossierId', value: dossierId }] : undefined;
|
||||
const queryParams = dossierId
|
||||
? [{ key: 'dossierId', value: dossierId }]
|
||||
: [{ key: 'dictionaryEntryType', value: dictionaryEntryType }];
|
||||
const url = `${this._defaultModelPath}/delete/${type}/${dossierTemplateId}`;
|
||||
return this._post(body, url, queryParams);
|
||||
return this._post(entries, url, queryParams);
|
||||
}
|
||||
}
|
||||
|
||||
@ -101,14 +101,15 @@ export class ManualAnnotationService extends GenericService<IManualAddResponse>
|
||||
}
|
||||
|
||||
addRecommendation(annotation: AnnotationWrapper, dossierId: string, fileId: string, comment = { text: 'Accepted Recommendation' }) {
|
||||
const manualRedactionEntry: IAddRedactionRequest = {};
|
||||
manualRedactionEntry.addToDictionary = true;
|
||||
const manualRedactionEntry: IAddRedactionRequest = {
|
||||
addToDictionary: true,
|
||||
// set the ID as reason, so we can hide the suggestion
|
||||
manualRedactionEntry.reason = annotation.annotationId;
|
||||
manualRedactionEntry.value = annotation.value;
|
||||
manualRedactionEntry.positions = annotation.positions;
|
||||
manualRedactionEntry.type = annotation.recommendationType;
|
||||
manualRedactionEntry.comment = comment;
|
||||
reason: annotation.annotationId,
|
||||
value: annotation.value,
|
||||
positions: annotation.positions,
|
||||
type: annotation.recommendationType,
|
||||
comment: comment,
|
||||
};
|
||||
return this.addAnnotation(manualRedactionEntry, dossierId, fileId);
|
||||
}
|
||||
|
||||
|
||||
@ -1,7 +1,7 @@
|
||||
{
|
||||
"ADMIN_CONTACT_NAME": null,
|
||||
"ADMIN_CONTACT_URL": null,
|
||||
"API_URL": "https://dev-08.iqser.cloud/redaction-gateway-v1",
|
||||
"API_URL": "https://dev-05.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-08.iqser.cloud/auth/realms/redaction",
|
||||
"OAUTH_URL": "https://dev-05.iqser.cloud/auth/realms/redaction",
|
||||
"RECENT_PERIOD_IN_HOURS": 24,
|
||||
"SELECTION_MODE": "structural",
|
||||
"MANUAL_BASE_URL": "https://docs.redactmanager.com/preview"
|
||||
|
||||
@ -6,7 +6,6 @@ export class Dictionary extends Entity<IDictionary> implements IDictionary {
|
||||
readonly caseInsensitive: boolean;
|
||||
readonly description: string;
|
||||
readonly dossierTemplateId?: string;
|
||||
entries: List;
|
||||
readonly hexColor?: string;
|
||||
readonly recommendationHexColor?: string;
|
||||
readonly hint: boolean;
|
||||
@ -14,23 +13,31 @@ export class Dictionary extends Entity<IDictionary> implements IDictionary {
|
||||
readonly rank?: number;
|
||||
readonly recommendation: boolean;
|
||||
readonly type: string;
|
||||
readonly typeId: string;
|
||||
readonly typeId?: string;
|
||||
readonly hasDictionary?: boolean;
|
||||
|
||||
constructor(dictionary: IDictionary, readonly virtual = false) {
|
||||
super(dictionary);
|
||||
this.addToDictionaryAction = !!dictionary.addToDictionaryAction;
|
||||
this.caseInsensitive = !!dictionary.caseInsensitive;
|
||||
this.description = dictionary.description ?? '';
|
||||
this.dossierTemplateId = dictionary.dossierTemplateId;
|
||||
this.entries = dictionary.entries ?? [];
|
||||
this.hexColor = dictionary.hexColor;
|
||||
this.recommendationHexColor = dictionary.recommendationHexColor;
|
||||
this.hint = !!dictionary.hint;
|
||||
this.label = dictionary.label ?? dictionary.type;
|
||||
this.rank = dictionary.rank;
|
||||
this.recommendation = !!dictionary.recommendation;
|
||||
this.type = dictionary.type;
|
||||
this.typeId = dictionary.typeId;
|
||||
entries: List;
|
||||
falsePositiveEntries: List;
|
||||
falseRecommendationEntries: List;
|
||||
|
||||
constructor(entity: IDictionary, readonly virtual = false) {
|
||||
super(entity);
|
||||
this.addToDictionaryAction = !!entity.addToDictionaryAction;
|
||||
this.caseInsensitive = !!entity.caseInsensitive;
|
||||
this.description = entity.description ?? '';
|
||||
this.dossierTemplateId = entity.dossierTemplateId;
|
||||
this.entries = entity.entries ?? [];
|
||||
this.falsePositiveEntries = entity.falsePositiveEntries ?? [];
|
||||
this.falseRecommendationEntries = entity.falseRecommendationEntries ?? [];
|
||||
this.hexColor = entity.hexColor;
|
||||
this.recommendationHexColor = entity.recommendationHexColor;
|
||||
this.hint = !!entity.hint;
|
||||
this.label = entity.label ?? entity.type;
|
||||
this.rank = entity.rank;
|
||||
this.recommendation = !!entity.recommendation;
|
||||
this.type = entity.type;
|
||||
this.typeId = entity.typeId;
|
||||
this.hasDictionary = entity.hasDictionary;
|
||||
}
|
||||
|
||||
get id(): string {
|
||||
|
||||
@ -29,6 +29,8 @@ export interface IDictionary {
|
||||
* The list of dictionary entries of an entry type.
|
||||
*/
|
||||
readonly entries?: List;
|
||||
readonly falsePositiveEntries?: List;
|
||||
readonly falseRecommendationEntries?: List;
|
||||
/**
|
||||
* The value of color must be a correct hex color
|
||||
*/
|
||||
@ -51,4 +53,6 @@ export interface IDictionary {
|
||||
readonly recommendation?: boolean;
|
||||
|
||||
readonly recommendationHexColor?: string;
|
||||
|
||||
readonly hasDictionary?: boolean;
|
||||
}
|
||||
|
||||
@ -39,6 +39,7 @@ export const isProcessingStatuses: List<ProcessingFileStatus> = [
|
||||
ProcessingFileStatuses.INDEXING,
|
||||
ProcessingFileStatuses.PROCESSING,
|
||||
ProcessingFileStatuses.ANALYSE,
|
||||
ProcessingFileStatuses.FULL_PROCESSING,
|
||||
] as const;
|
||||
|
||||
export const isFullProcessingStatuses: List<ProcessingFileStatus> = [
|
||||
|
||||
@ -1,9 +1,11 @@
|
||||
import { IRectangle } from '../geometry';
|
||||
import { List } from '@iqser/common-ui';
|
||||
import { DictionaryEntryType } from './dictionary-entry-types';
|
||||
|
||||
export interface IAddRedactionRequest {
|
||||
addToDictionary?: boolean;
|
||||
addToDossierDictionary?: boolean;
|
||||
dictionaryEntryType?: DictionaryEntryType;
|
||||
comment?: { text: string };
|
||||
legalBasis?: string;
|
||||
positions?: List<IRectangle>;
|
||||
|
||||
@ -0,0 +1,24 @@
|
||||
import { KeysOf } from '@iqser/common-ui';
|
||||
import { Dictionary } from '../dictionaries';
|
||||
|
||||
export type DictionaryEntryType = 'ENTRY' | 'FALSE_POSITIVE' | 'FALSE_RECOMMENDATION';
|
||||
|
||||
export const DictionaryEntryTypes = {
|
||||
ENTRY: 'ENTRY' as DictionaryEntryType,
|
||||
FALSE_POSITIVE: 'FALSE_POSITIVE' as DictionaryEntryType,
|
||||
FALSE_RECOMMENDATION: 'FALSE_RECOMMENDATION' as DictionaryEntryType,
|
||||
};
|
||||
|
||||
export type DictionaryType = 'dictionary' | 'false-positive' | 'false-recommendations';
|
||||
|
||||
export const DICTIONARY_TYPE_KEY_MAP: { [key in DictionaryType]: KeysOf<Dictionary> } = {
|
||||
dictionary: 'entries',
|
||||
'false-positive': 'falsePositiveEntries',
|
||||
'false-recommendations': 'falseRecommendationEntries',
|
||||
};
|
||||
|
||||
export const DICTIONARY_TO_ENTRY_TYPE_MAP: { [key in DictionaryType]: DictionaryEntryType } = {
|
||||
dictionary: DictionaryEntryTypes.ENTRY,
|
||||
'false-positive': DictionaryEntryTypes.FALSE_POSITIVE,
|
||||
'false-recommendations': DictionaryEntryTypes.FALSE_RECOMMENDATION,
|
||||
};
|
||||
@ -11,3 +11,4 @@ export * from './approve-request';
|
||||
export * from './image-recategorization.request';
|
||||
export * from './resize.request';
|
||||
export * from './manual-change';
|
||||
export * from './dictionary-entry-types';
|
||||
|
||||
Loading…
x
Reference in New Issue
Block a user