False positive & recommendations

This commit is contained in:
Adina Țeudan 2022-03-23 15:58:00 +02:00
parent e5686441cf
commit 52681330f5
23 changed files with 192 additions and 101 deletions

View File

@ -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 =

View File

@ -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;
}
}

View File

@ -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({

View File

@ -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();

View File

@ -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>

View File

@ -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],
});
}
}

View File

@ -28,7 +28,3 @@
iqser-status-bar {
margin-left: 2px;
}
.spinning-icon {
margin: 0 12px 0 11px;
}

View File

@ -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),
);
}
}

View File

@ -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 =

View File

@ -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(

View File

@ -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;

View File

@ -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 }}

View File

@ -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;
}

View File

@ -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;
}
}

View File

@ -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);
}
}

View File

@ -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;
// 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;
const manualRedactionEntry: IAddRedactionRequest = {
addToDictionary: true,
// set the ID as reason, so we can hide the suggestion
reason: annotation.annotationId,
value: annotation.value,
positions: annotation.positions,
type: annotation.recommendationType,
comment: comment,
};
return this.addAnnotation(manualRedactionEntry, dossierId, fileId);
}

View File

@ -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"

View File

@ -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 {

View File

@ -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;
}

View File

@ -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> = [

View File

@ -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>;

View File

@ -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,
};

View File

@ -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';