Merge branch 'master' into VM/RED-2853
This commit is contained in:
commit
816ebfb954
@ -72,6 +72,8 @@ const components = [AppComponent, AuthErrorComponent, NotificationsComponent, Sp
|
||||
closeButton: true,
|
||||
enableHtml: true,
|
||||
toastComponent: ToastComponent,
|
||||
preventDuplicates: true,
|
||||
resetTimeoutOnDuplicate: true,
|
||||
}),
|
||||
TranslateModule.forRoot({
|
||||
loader: {
|
||||
|
||||
@ -1,13 +1,14 @@
|
||||
import { RedactionLogEntryWrapper } from './redaction-log-entry.wrapper';
|
||||
import { annotationTypesTranslations } from '../../translations/annotation-types-translations';
|
||||
import { marker as _ } from '@biesbjerg/ngx-translate-extract-marker';
|
||||
import { IComment, IPoint, IRectangle } from '@red/domain';
|
||||
import { IComment, IManualChange, IPoint, IRectangle, LogEntryStatus, ManualRedactionType } from '@red/domain';
|
||||
import { RedactionLogEntry } from '@models/file/redaction-log.entry';
|
||||
|
||||
export type AnnotationSuperType =
|
||||
| 'suggestion-change-legal-basis'
|
||||
| 'suggestion-recategorize-image'
|
||||
| 'suggestion-add-dictionary'
|
||||
| 'suggestion-force-redaction'
|
||||
| 'suggestion-force-hint'
|
||||
| 'suggestion-resize'
|
||||
| 'suggestion-remove-dictionary'
|
||||
| 'suggestion-add'
|
||||
@ -44,15 +45,14 @@ export class AnnotationWrapper {
|
||||
legalBasisChangeValue?: string;
|
||||
resizing?: boolean;
|
||||
rectangle?: boolean;
|
||||
hintDictionary?: boolean;
|
||||
section?: string;
|
||||
reference: Array<string>;
|
||||
|
||||
manual?: boolean;
|
||||
|
||||
image?: boolean;
|
||||
manual?: boolean;
|
||||
hidden?: boolean;
|
||||
force?: boolean;
|
||||
pending?: boolean;
|
||||
hintDictionary?: boolean;
|
||||
|
||||
textAfter?: string;
|
||||
textBefore?: string;
|
||||
@ -64,11 +64,10 @@ export class AnnotationWrapper {
|
||||
hasBeenResized: boolean;
|
||||
hasBeenRecategorized: boolean;
|
||||
hasLegalBasisChanged: boolean;
|
||||
hasBeenForced: boolean;
|
||||
hasBeenForcedHint: boolean;
|
||||
hasBeenForcedRedaction: boolean;
|
||||
hasBeenRemovedByManualOverride: boolean;
|
||||
|
||||
private _origin: RedactionLogEntryWrapper;
|
||||
|
||||
get isChangeLogRemoved() {
|
||||
return this.changeLogType === 'REMOVED';
|
||||
}
|
||||
@ -204,7 +203,8 @@ export class AnnotationWrapper {
|
||||
this.hasBeenResized ||
|
||||
this.hasBeenRecategorized ||
|
||||
this.hasLegalBasisChanged ||
|
||||
this.hasBeenForced ||
|
||||
this.hasBeenForcedHint ||
|
||||
this.hasBeenForcedRedaction ||
|
||||
this.hasBeenRemovedByManualOverride
|
||||
);
|
||||
}
|
||||
@ -232,212 +232,91 @@ export class AnnotationWrapper {
|
||||
get legalBasis() {
|
||||
return this.legalBasisChangeValue || this.legalBasisValue;
|
||||
}
|
||||
|
||||
static fromData(redactionLogEntry?: RedactionLogEntryWrapper) {
|
||||
redactionLogEntry: any;
|
||||
static fromData(redactionLogEntry?: RedactionLogEntry) {
|
||||
const annotationWrapper = new AnnotationWrapper();
|
||||
|
||||
annotationWrapper._origin = redactionLogEntry;
|
||||
|
||||
annotationWrapper.annotationId = redactionLogEntry.id;
|
||||
annotationWrapper.isChangeLogEntry = redactionLogEntry.isChangeLogEntry;
|
||||
annotationWrapper.changeLogType = redactionLogEntry.changeLogType;
|
||||
annotationWrapper.redaction = redactionLogEntry.redacted;
|
||||
annotationWrapper.hint = redactionLogEntry.hint;
|
||||
annotationWrapper.typeValue = redactionLogEntry.type;
|
||||
annotationWrapper.recategorizationType = redactionLogEntry.recategorizationType;
|
||||
annotationWrapper.value = redactionLogEntry.value;
|
||||
annotationWrapper.firstTopLeftPoint = redactionLogEntry.positions[0]?.topLeft;
|
||||
annotationWrapper.pageNumber = redactionLogEntry.positions[0]?.page;
|
||||
annotationWrapper.positions = redactionLogEntry.positions;
|
||||
annotationWrapper.status = redactionLogEntry.status;
|
||||
annotationWrapper.textBefore = redactionLogEntry.textBefore;
|
||||
annotationWrapper.textAfter = redactionLogEntry.textAfter;
|
||||
annotationWrapper.dictionaryOperation = redactionLogEntry.dictionaryEntry;
|
||||
annotationWrapper.image = redactionLogEntry.image;
|
||||
annotationWrapper.legalBasisChangeValue = redactionLogEntry.legalBasisChangeValue;
|
||||
annotationWrapper.legalBasisValue = redactionLogEntry.legalBasis;
|
||||
annotationWrapper.comments = redactionLogEntry.comments || [];
|
||||
annotationWrapper.manual = redactionLogEntry.manual;
|
||||
annotationWrapper.manual = redactionLogEntry.manualChanges?.length > 0;
|
||||
annotationWrapper.engines = redactionLogEntry.engines;
|
||||
annotationWrapper.section = redactionLogEntry.section;
|
||||
annotationWrapper.reference = redactionLogEntry.reference || [];
|
||||
annotationWrapper.rectangle = redactionLogEntry.rectangle;
|
||||
annotationWrapper.hasBeenResized = redactionLogEntry.hasBeenResized;
|
||||
annotationWrapper.hasBeenRecategorized = redactionLogEntry.hasBeenRecategorized;
|
||||
annotationWrapper.hasLegalBasisChanged = redactionLogEntry.hasLegalBasisChanged;
|
||||
annotationWrapper.hasBeenForced = redactionLogEntry.hasBeenForced;
|
||||
annotationWrapper.hasBeenRemovedByManualOverride = redactionLogEntry.hasBeenRemovedByManualOverride;
|
||||
annotationWrapper.hintDictionary = redactionLogEntry.hintDictionary;
|
||||
annotationWrapper.hasBeenResized = !!redactionLogEntry.manualChanges.find(
|
||||
c => c.manualRedactionType === ManualRedactionType.RESIZE && c.annotationStatus === LogEntryStatus.APPROVED,
|
||||
);
|
||||
annotationWrapper.hasBeenRecategorized = !!redactionLogEntry.manualChanges.find(
|
||||
c => c.manualRedactionType === ManualRedactionType.RECATEGORIZE && c.annotationStatus === LogEntryStatus.APPROVED,
|
||||
);
|
||||
annotationWrapper.hasLegalBasisChanged = !!redactionLogEntry.manualChanges.find(
|
||||
c => c.manualRedactionType === ManualRedactionType.LEGAL_BASIS_CHANGE && c.annotationStatus === LogEntryStatus.APPROVED,
|
||||
);
|
||||
annotationWrapper.hasBeenForcedHint = !!redactionLogEntry.manualChanges.find(
|
||||
c => c.manualRedactionType === ManualRedactionType.FORCE_HINT && c.annotationStatus === LogEntryStatus.APPROVED,
|
||||
);
|
||||
annotationWrapper.hasBeenForcedRedaction = !!redactionLogEntry.manualChanges.find(
|
||||
c => c.manualRedactionType === ManualRedactionType.FORCE_REDACT && c.annotationStatus === LogEntryStatus.APPROVED,
|
||||
);
|
||||
annotationWrapper.hasBeenRemovedByManualOverride = !!redactionLogEntry.manualChanges.find(
|
||||
c => c.manualRedactionType === ManualRedactionType.REMOVE_LOCALLY && c.annotationStatus === LogEntryStatus.APPROVED,
|
||||
);
|
||||
|
||||
this._createContent(annotationWrapper, redactionLogEntry);
|
||||
this._setSuperType(annotationWrapper, redactionLogEntry);
|
||||
this._handleRecommendations(annotationWrapper, redactionLogEntry);
|
||||
annotationWrapper.typeLabel = annotationTypesTranslations[annotationWrapper.superType];
|
||||
|
||||
annotationWrapper.redactionLogEntry = redactionLogEntry;
|
||||
return annotationWrapper;
|
||||
}
|
||||
|
||||
private static _handleRecommendations(annotationWrapper: AnnotationWrapper, redactionLogEntry: RedactionLogEntryWrapper) {
|
||||
private static _handleRecommendations(annotationWrapper: AnnotationWrapper, redactionLogEntry: RedactionLogEntry) {
|
||||
if (annotationWrapper.superType === 'recommendation') {
|
||||
annotationWrapper.recommendationType = redactionLogEntry.type.substr('recommendation_'.length);
|
||||
}
|
||||
}
|
||||
|
||||
private static _setSuperType(annotationWrapper: AnnotationWrapper, redactionLogEntryWrapper: RedactionLogEntryWrapper) {
|
||||
private static _setSuperType(annotationWrapper: AnnotationWrapper, redactionLogEntryWrapper: RedactionLogEntry) {
|
||||
if (redactionLogEntryWrapper.recommendation) {
|
||||
annotationWrapper.superType = 'recommendation';
|
||||
return;
|
||||
}
|
||||
|
||||
if (redactionLogEntryWrapper.manualRedactionType === 'RESIZE') {
|
||||
if (redactionLogEntryWrapper.status === 'REQUESTED') {
|
||||
annotationWrapper.superType = 'suggestion-resize';
|
||||
return;
|
||||
}
|
||||
}
|
||||
if (redactionLogEntryWrapper.manualChanges.length) {
|
||||
const lastManualChange = redactionLogEntryWrapper.manualChanges[redactionLogEntryWrapper.manualChanges.length - 1];
|
||||
|
||||
if (redactionLogEntryWrapper.manualRedactionType === 'FORCE_REDACT') {
|
||||
annotationWrapper.force = true;
|
||||
annotationWrapper.pending = !lastManualChange.processed;
|
||||
|
||||
if (redactionLogEntryWrapper.status === 'REQUESTED') {
|
||||
annotationWrapper.superType = 'suggestion-force-redaction';
|
||||
} else if (redactionLogEntryWrapper.status === 'APPROVED') {
|
||||
annotationWrapper.superType = redactionLogEntryWrapper.hint ? 'hint' : 'redaction';
|
||||
} else {
|
||||
annotationWrapper.superType = 'skipped';
|
||||
}
|
||||
return;
|
||||
}
|
||||
|
||||
if (annotationWrapper.type?.toLowerCase() === 'false_positive') {
|
||||
if (redactionLogEntryWrapper.status === 'REQUESTED') {
|
||||
annotationWrapper.superType = 'suggestion-add-dictionary';
|
||||
return;
|
||||
}
|
||||
if (redactionLogEntryWrapper.status === 'DECLINED') {
|
||||
annotationWrapper.superType = 'declined-suggestion';
|
||||
return;
|
||||
}
|
||||
if (!redactionLogEntryWrapper.manual) {
|
||||
annotationWrapper.superType = 'skipped';
|
||||
}
|
||||
return;
|
||||
}
|
||||
|
||||
if (redactionLogEntryWrapper.type === 'manual') {
|
||||
if (redactionLogEntryWrapper.status === 'REQUESTED') {
|
||||
annotationWrapper.superType = 'suggestion-add';
|
||||
return;
|
||||
}
|
||||
if (redactionLogEntryWrapper.status === 'APPROVED') {
|
||||
annotationWrapper.superType = 'manual-redaction';
|
||||
return;
|
||||
}
|
||||
if (redactionLogEntryWrapper.status === 'DECLINED') {
|
||||
annotationWrapper.superType = 'declined-suggestion';
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
if (redactionLogEntryWrapper.manualRedactionType === 'LEGAL_BASIS_CHANGE') {
|
||||
if (redactionLogEntryWrapper.status === 'REQUESTED') {
|
||||
annotationWrapper.superType = 'suggestion-change-legal-basis';
|
||||
} else {
|
||||
annotationWrapper.superType = AnnotationWrapper._selectSuperType(redactionLogEntryWrapper, lastManualChange);
|
||||
} else {
|
||||
if (redactionLogEntryWrapper.recommendation) {
|
||||
annotationWrapper.superType = 'recommendation';
|
||||
} else if (redactionLogEntryWrapper.redacted) {
|
||||
annotationWrapper.superType = 'redaction';
|
||||
}
|
||||
return;
|
||||
}
|
||||
|
||||
if (redactionLogEntryWrapper.manualRedactionType === 'RECATEGORIZE') {
|
||||
if (redactionLogEntryWrapper.status === 'REQUESTED') {
|
||||
annotationWrapper.superType = 'suggestion-recategorize-image';
|
||||
return;
|
||||
}
|
||||
if (redactionLogEntryWrapper.status === 'APPROVED') {
|
||||
annotationWrapper.superType = annotationWrapper.redaction ? 'redaction' : annotationWrapper.hint ? 'hint' : 'skipped';
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
if (redactionLogEntryWrapper.manualRedactionType === 'ADD') {
|
||||
if (redactionLogEntryWrapper.dictionaryEntry) {
|
||||
if (redactionLogEntryWrapper.status === 'REQUESTED') {
|
||||
annotationWrapper.superType = 'suggestion-add-dictionary';
|
||||
return;
|
||||
}
|
||||
if (redactionLogEntryWrapper.status === 'DECLINED') {
|
||||
annotationWrapper.superType = 'declined-suggestion';
|
||||
return;
|
||||
}
|
||||
} else if (redactionLogEntryWrapper.hint) {
|
||||
annotationWrapper.superType = 'hint';
|
||||
} else {
|
||||
if (redactionLogEntryWrapper.status === 'REQUESTED') {
|
||||
annotationWrapper.superType = 'suggestion-add';
|
||||
return;
|
||||
}
|
||||
if (redactionLogEntryWrapper.status === 'APPROVED') {
|
||||
annotationWrapper.superType = 'manual-redaction';
|
||||
return;
|
||||
}
|
||||
if (redactionLogEntryWrapper.status === 'DECLINED') {
|
||||
annotationWrapper.superType = 'declined-suggestion';
|
||||
return;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if (!redactionLogEntryWrapper.redacted && !redactionLogEntryWrapper.hint) {
|
||||
if (redactionLogEntryWrapper.status === 'REQUESTED') {
|
||||
if (redactionLogEntryWrapper.dictionaryEntry) {
|
||||
annotationWrapper.superType =
|
||||
redactionLogEntryWrapper.manualRedactionType === 'ADD'
|
||||
? 'suggestion-add-dictionary'
|
||||
: 'suggestion-remove-dictionary';
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if (redactionLogEntryWrapper.redacted || redactionLogEntryWrapper.hint) {
|
||||
if (redactionLogEntryWrapper.status === 'REQUESTED') {
|
||||
if (redactionLogEntryWrapper.dictionaryEntry) {
|
||||
annotationWrapper.superType =
|
||||
redactionLogEntryWrapper.manualRedactionType === 'ADD'
|
||||
? 'suggestion-add-dictionary'
|
||||
: 'suggestion-remove-dictionary';
|
||||
} else {
|
||||
annotationWrapper.superType =
|
||||
redactionLogEntryWrapper.manualRedactionType === 'ADD' ? 'suggestion-add' : 'suggestion-remove';
|
||||
}
|
||||
return;
|
||||
}
|
||||
|
||||
if (redactionLogEntryWrapper.status === 'APPROVED') {
|
||||
if (redactionLogEntryWrapper.dictionaryEntry) {
|
||||
annotationWrapper.superType = annotationWrapper.redaction ? 'redaction' : annotationWrapper.hint ? 'hint' : 'skipped';
|
||||
} else {
|
||||
annotationWrapper.superType =
|
||||
redactionLogEntryWrapper.manualRedactionType === 'ADD'
|
||||
? 'manual-redaction'
|
||||
: annotationWrapper.redaction
|
||||
? 'redaction'
|
||||
: annotationWrapper.hint
|
||||
? 'hint'
|
||||
: 'skipped';
|
||||
}
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
if (!annotationWrapper.superType) {
|
||||
annotationWrapper.superType = annotationWrapper.redaction ? 'redaction' : annotationWrapper.hint ? 'hint' : 'skipped';
|
||||
}
|
||||
if (annotationWrapper.superType === 'skipped') {
|
||||
if (redactionLogEntryWrapper.hintDictionary) {
|
||||
annotationWrapper.superType = 'ignored-hint';
|
||||
annotationWrapper.superType = 'skipped';
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private static _createContent(annotationWrapper: AnnotationWrapper, entry: RedactionLogEntryWrapper) {
|
||||
private static _createContent(annotationWrapper: AnnotationWrapper, entry: RedactionLogEntry) {
|
||||
let content = '';
|
||||
if (entry.matchedRule) {
|
||||
content += `Rule ${entry.matchedRule} matched \n\n`;
|
||||
@ -459,7 +338,7 @@ export class AnnotationWrapper {
|
||||
annotationWrapper.content = content;
|
||||
}
|
||||
|
||||
private static _getShortContent(annotationWrapper: AnnotationWrapper, entry: RedactionLogEntryWrapper) {
|
||||
private static _getShortContent(annotationWrapper: AnnotationWrapper, entry: RedactionLogEntry) {
|
||||
if (annotationWrapper.legalBasis) {
|
||||
const lb = entry.legalBasisList?.find(lbm => lbm.reason.toLowerCase().includes(annotationWrapper.legalBasis.toLowerCase()));
|
||||
if (lb) {
|
||||
@ -469,4 +348,121 @@ export class AnnotationWrapper {
|
||||
|
||||
return annotationWrapper.legalBasis;
|
||||
}
|
||||
|
||||
// | 'suggestion-change-legal-basis'
|
||||
// | 'suggestion-recategorize-image'
|
||||
// | 'suggestion-add-dictionary'
|
||||
// | 'suggestion-force-redaction'
|
||||
// | 'suggestion-resize'
|
||||
// | 'suggestion-remove-dictionary'
|
||||
// | 'suggestion-add'
|
||||
// | 'suggestion-remove'
|
||||
// | 'ignored-hint'
|
||||
// | 'skipped'
|
||||
// | 'redaction'
|
||||
// | 'manual-redaction'
|
||||
// | 'recommendation'
|
||||
// | 'hint'
|
||||
// | 'declined-suggestion';
|
||||
private static _selectSuperType(redactionLogEntry: RedactionLogEntry, lastManualChange: IManualChange): AnnotationSuperType {
|
||||
switch (lastManualChange.manualRedactionType) {
|
||||
case ManualRedactionType.ADD_LOCALLY:
|
||||
switch (lastManualChange.annotationStatus) {
|
||||
case LogEntryStatus.APPROVED:
|
||||
return 'manual-redaction';
|
||||
case LogEntryStatus.DECLINED:
|
||||
return 'declined-suggestion';
|
||||
case LogEntryStatus.REQUESTED:
|
||||
return 'suggestion-add';
|
||||
}
|
||||
case ManualRedactionType.ADD_TO_DICTIONARY:
|
||||
switch (lastManualChange.annotationStatus) {
|
||||
case LogEntryStatus.APPROVED:
|
||||
// TODO
|
||||
return 'redaction';
|
||||
case LogEntryStatus.DECLINED:
|
||||
return 'declined-suggestion';
|
||||
case LogEntryStatus.REQUESTED:
|
||||
return 'suggestion-add-dictionary';
|
||||
}
|
||||
break;
|
||||
case ManualRedactionType.REMOVE_LOCALLY:
|
||||
switch (lastManualChange.annotationStatus) {
|
||||
case LogEntryStatus.APPROVED:
|
||||
return 'skipped';
|
||||
case LogEntryStatus.DECLINED:
|
||||
return 'declined-suggestion';
|
||||
case LogEntryStatus.REQUESTED:
|
||||
return 'suggestion-remove';
|
||||
}
|
||||
break;
|
||||
case ManualRedactionType.REMOVE_FROM_DICTIONARY:
|
||||
switch (lastManualChange.annotationStatus) {
|
||||
case LogEntryStatus.APPROVED:
|
||||
// TODO
|
||||
return 'skipped';
|
||||
case LogEntryStatus.DECLINED:
|
||||
return 'declined-suggestion';
|
||||
case LogEntryStatus.REQUESTED:
|
||||
return 'suggestion-remove-dictionary';
|
||||
}
|
||||
break;
|
||||
case ManualRedactionType.FORCE_REDACT:
|
||||
switch (lastManualChange.annotationStatus) {
|
||||
case LogEntryStatus.APPROVED:
|
||||
// TODO
|
||||
return 'redaction';
|
||||
case LogEntryStatus.DECLINED:
|
||||
return 'declined-suggestion';
|
||||
case LogEntryStatus.REQUESTED:
|
||||
return 'suggestion-force-redaction';
|
||||
}
|
||||
break;
|
||||
case ManualRedactionType.FORCE_HINT:
|
||||
switch (lastManualChange.annotationStatus) {
|
||||
case LogEntryStatus.APPROVED:
|
||||
// TODO
|
||||
return 'redaction';
|
||||
case LogEntryStatus.DECLINED:
|
||||
return 'declined-suggestion';
|
||||
case LogEntryStatus.REQUESTED:
|
||||
return 'suggestion-force-hint';
|
||||
}
|
||||
break;
|
||||
case ManualRedactionType.RECATEGORIZE:
|
||||
switch (lastManualChange.annotationStatus) {
|
||||
case LogEntryStatus.APPROVED:
|
||||
return redactionLogEntry.redacted ? 'redaction' : 'hint';
|
||||
case LogEntryStatus.DECLINED:
|
||||
return 'declined-suggestion';
|
||||
case LogEntryStatus.REQUESTED:
|
||||
return 'suggestion-recategorize-image';
|
||||
}
|
||||
break;
|
||||
case ManualRedactionType.LEGAL_BASIS_CHANGE:
|
||||
switch (lastManualChange.annotationStatus) {
|
||||
case LogEntryStatus.APPROVED:
|
||||
return 'redaction';
|
||||
case LogEntryStatus.DECLINED:
|
||||
return 'declined-suggestion';
|
||||
case LogEntryStatus.REQUESTED:
|
||||
return 'suggestion-change-legal-basis';
|
||||
}
|
||||
break;
|
||||
case ManualRedactionType.RESIZE:
|
||||
switch (lastManualChange.annotationStatus) {
|
||||
case LogEntryStatus.APPROVED:
|
||||
if (redactionLogEntry.recommendation) {
|
||||
return 'recommendation';
|
||||
} else {
|
||||
return redactionLogEntry.redacted ? 'redaction' : 'hint';
|
||||
}
|
||||
case LogEntryStatus.DECLINED:
|
||||
return 'declined-suggestion';
|
||||
case LogEntryStatus.REQUESTED:
|
||||
return 'suggestion-resize';
|
||||
}
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@ -1,8 +1,18 @@
|
||||
import { Dictionary, File, IRedactionLog, IRedactionLogEntry, IViewedPage, ViewMode } from '@red/domain';
|
||||
import {
|
||||
ChangeType,
|
||||
Dictionary,
|
||||
File,
|
||||
IRedactionLog,
|
||||
IRedactionLogEntry,
|
||||
IViewedPage,
|
||||
LogEntryStatus,
|
||||
ManualRedactionType,
|
||||
ViewMode,
|
||||
} from '@red/domain';
|
||||
import { AnnotationWrapper } from './annotation.wrapper';
|
||||
import { RedactionLogEntryWrapper } from './redaction-log-entry.wrapper';
|
||||
import * as moment from 'moment';
|
||||
import { BehaviorSubject } from 'rxjs';
|
||||
import { RedactionLogEntry } from './redaction-log.entry';
|
||||
|
||||
export class FileDataModel {
|
||||
static readonly DELTA_VIEW_TIME = 10 * 60 * 1000; // 10 minutes;
|
||||
@ -54,7 +64,7 @@ export class FileDataModel {
|
||||
}
|
||||
|
||||
private _buildAllAnnotations() {
|
||||
const entries: RedactionLogEntryWrapper[] = this._convertData();
|
||||
const entries: RedactionLogEntry[] = this._convertData();
|
||||
|
||||
const previousAnnotations = this.allAnnotations || [];
|
||||
this.allAnnotations = entries
|
||||
@ -77,31 +87,30 @@ export class FileDataModel {
|
||||
});
|
||||
}
|
||||
|
||||
private _convertData(): RedactionLogEntryWrapper[] {
|
||||
let result: RedactionLogEntryWrapper[] = [];
|
||||
private _convertData(): RedactionLogEntry[] {
|
||||
let result: RedactionLogEntry[] = [];
|
||||
|
||||
const reasonAnnotationIds: { [key: string]: RedactionLogEntryWrapper[] } = {};
|
||||
const reasonAnnotationIds: { [key: string]: RedactionLogEntry[] } = {};
|
||||
this.redactionLog.redactionLogEntry?.forEach(redactionLogEntry => {
|
||||
// copy the redactionLog Entry
|
||||
const redactionLogEntryWrapper: RedactionLogEntryWrapper = {};
|
||||
Object.assign(redactionLogEntryWrapper, redactionLogEntry);
|
||||
redactionLogEntryWrapper.type = redactionLogEntry.type;
|
||||
redactionLogEntryWrapper.hintDictionary = this._dictionaryData[redactionLogEntry.type].hint;
|
||||
|
||||
this._isChangeLogEntry(redactionLogEntry, redactionLogEntryWrapper);
|
||||
const changeLogValues = this.getChangeLogValues(redactionLogEntry);
|
||||
const redactionLogEntryWrapper: RedactionLogEntry = new RedactionLogEntry(
|
||||
redactionLogEntry,
|
||||
changeLogValues.changeLogType,
|
||||
changeLogValues.isChangeLogEntry,
|
||||
changeLogValues.hidden,
|
||||
this._dictionaryData[redactionLogEntry.type].hint,
|
||||
this.redactionLog.legalBasis,
|
||||
);
|
||||
|
||||
if (
|
||||
redactionLogEntryWrapper.status === 'DECLINED' &&
|
||||
redactionLogEntryWrapper.manualRedactionType === 'ADD' &&
|
||||
redactionLogEntryWrapper.type === 'false_positive'
|
||||
redactionLogEntry.manualChanges.find(
|
||||
mc =>
|
||||
mc.manualRedactionType === ManualRedactionType.ADD_TO_DICTIONARY &&
|
||||
(mc.annotationStatus === LogEntryStatus.APPROVED || mc.annotationStatus === LogEntryStatus.REQUESTED),
|
||||
)
|
||||
) {
|
||||
// ignore these
|
||||
return;
|
||||
} else {
|
||||
result.push(redactionLogEntryWrapper);
|
||||
}
|
||||
|
||||
if (redactionLogEntry.manual && (redactionLogEntry.status === 'APPROVED' || redactionLogEntry.status === 'REQUESTED')) {
|
||||
// for dictionary entries -> I.E accepted recommendations or false positives,
|
||||
// check reason
|
||||
if (!reasonAnnotationIds[redactionLogEntry.reason]) {
|
||||
@ -110,6 +119,8 @@ export class FileDataModel {
|
||||
reasonAnnotationIds[redactionLogEntry.reason].push(redactionLogEntryWrapper);
|
||||
}
|
||||
}
|
||||
|
||||
result.push(redactionLogEntryWrapper);
|
||||
});
|
||||
|
||||
const reasonKeys = Object.keys(reasonAnnotationIds);
|
||||
@ -123,16 +134,16 @@ export class FileDataModel {
|
||||
return !matched;
|
||||
});
|
||||
|
||||
result.forEach(redactionLogEntry => {
|
||||
redactionLogEntry.legalBasisList = this.redactionLog.legalBasis;
|
||||
});
|
||||
|
||||
result = result.filter(r => !r.hidden);
|
||||
|
||||
return result;
|
||||
}
|
||||
|
||||
private _isChangeLogEntry(redactionLogEntry: IRedactionLogEntry, wrapper: RedactionLogEntryWrapper) {
|
||||
private getChangeLogValues(redactionLogEntry: IRedactionLogEntry): {
|
||||
hidden: boolean;
|
||||
changeLogType: ChangeType;
|
||||
isChangeLogEntry: boolean;
|
||||
} {
|
||||
if (this._file.numberOfAnalyses > 1) {
|
||||
const viableChanges = redactionLogEntry.changes.filter(c => c.analysisNumber > 1);
|
||||
viableChanges.sort((a, b) => moment(a.dateTime).valueOf() - moment(b.dateTime).valueOf());
|
||||
@ -150,20 +161,35 @@ export class FileDataModel {
|
||||
// at least one unseen change
|
||||
if (relevantChanges.length > 0) {
|
||||
// at least 1 relevant change
|
||||
wrapper.changeLogType = relevantChanges[relevantChanges.length - 1].type;
|
||||
wrapper.isChangeLogEntry = true;
|
||||
viewedPage.showAsUnseen = moment(viewedPage.viewedTime).valueOf() < moment(lastChange.dateTime).valueOf();
|
||||
this.hasChangeLog$.next(true);
|
||||
return {
|
||||
changeLogType: relevantChanges[relevantChanges.length - 1].type,
|
||||
isChangeLogEntry: true,
|
||||
hidden: false,
|
||||
};
|
||||
} else {
|
||||
// no relevant changes - hide removed anyway
|
||||
wrapper.isChangeLogEntry = false;
|
||||
wrapper.hidden = lastChange && lastChange.type === 'REMOVED';
|
||||
return {
|
||||
changeLogType: null,
|
||||
isChangeLogEntry: false,
|
||||
hidden: lastChange && lastChange.type === 'REMOVED',
|
||||
};
|
||||
}
|
||||
} else {
|
||||
// Page doesn't have a view-time
|
||||
wrapper.isChangeLogEntry = false;
|
||||
wrapper.hidden = lastChange && lastChange.type === 'REMOVED';
|
||||
return {
|
||||
changeLogType: null,
|
||||
isChangeLogEntry: false,
|
||||
hidden: lastChange && lastChange.type === 'REMOVED',
|
||||
};
|
||||
}
|
||||
} else {
|
||||
return {
|
||||
changeLogType: null,
|
||||
isChangeLogEntry: false,
|
||||
hidden: false,
|
||||
};
|
||||
}
|
||||
// console.log(wrapper.changeLogType, wrapper.hidden, wrapper.isChangeLogEntry, wrapper.value, lastChange);
|
||||
}
|
||||
|
||||
@ -1,56 +0,0 @@
|
||||
import { IChange, IComment, ILegalBasis, IRectangle } from '@red/domain';
|
||||
|
||||
export interface RedactionLogEntryWrapper {
|
||||
changes?: Array<IChange>;
|
||||
dossierDictionaryEntry?: boolean;
|
||||
endOffset?: number;
|
||||
excluded?: boolean;
|
||||
imageHasTransparency?: boolean;
|
||||
manualRedactionUserId?: string;
|
||||
reference?: Array<string>;
|
||||
startOffset?: number;
|
||||
type?: string;
|
||||
rectangle?: boolean;
|
||||
hintDictionary?: boolean;
|
||||
|
||||
color?: Array<number>;
|
||||
dictionaryEntry?: boolean;
|
||||
hint?: boolean;
|
||||
id?: string;
|
||||
legalBasis?: string;
|
||||
manual?: boolean;
|
||||
manualRedactionType?: 'ADD' | 'REMOVE' | 'LEGAL_BASIS_CHANGE' | 'FORCE_REDACT' | 'RECATEGORIZE' | 'RESIZE';
|
||||
matchedRule?: number;
|
||||
positions?: Array<IRectangle>;
|
||||
reason?: string;
|
||||
redacted?: boolean;
|
||||
section?: string;
|
||||
sectionNumber?: number;
|
||||
status?: 'REQUESTED' | 'APPROVED' | 'DECLINED';
|
||||
textAfter?: string;
|
||||
textBefore?: string;
|
||||
value?: string;
|
||||
image?: boolean;
|
||||
legalBasisList?: ILegalBasis[];
|
||||
|
||||
recommendation?: boolean;
|
||||
recommendationAnnotationId?: string;
|
||||
|
||||
hidden?: boolean;
|
||||
|
||||
userId?: string;
|
||||
comments?: IComment[];
|
||||
|
||||
isChangeLogEntry?: boolean;
|
||||
changeLogType?: 'ADDED' | 'REMOVED' | 'CHANGED';
|
||||
|
||||
recategorizationType?: string;
|
||||
legalBasisChangeValue?: string;
|
||||
engines?: string[];
|
||||
|
||||
hasBeenResized?: boolean;
|
||||
hasBeenRecategorized?: boolean;
|
||||
hasLegalBasisChanged?: boolean;
|
||||
hasBeenForced?: boolean;
|
||||
hasBeenRemovedByManualOverride?: boolean;
|
||||
}
|
||||
81
apps/red-ui/src/app/models/file/redaction-log.entry.ts
Normal file
81
apps/red-ui/src/app/models/file/redaction-log.entry.ts
Normal file
@ -0,0 +1,81 @@
|
||||
import { IChange, IComment, ILegalBasis, IManualChange, IRectangle, IRedactionLogEntry, LogEntryEngine } from '@red/domain';
|
||||
|
||||
export class RedactionLogEntry implements IRedactionLogEntry {
|
||||
readonly changes?: IChange[];
|
||||
readonly manualChanges?: IManualChange[];
|
||||
readonly color?: number[];
|
||||
readonly comments?: IComment[];
|
||||
readonly dictionaryEntry?: boolean;
|
||||
readonly dossierDictionaryEntry?: boolean;
|
||||
readonly endOffset?: number;
|
||||
readonly engines?: LogEntryEngine[];
|
||||
readonly excluded?: boolean;
|
||||
readonly hint?: boolean;
|
||||
readonly rectangle?: boolean;
|
||||
readonly id?: string;
|
||||
readonly image?: boolean;
|
||||
readonly imageHasTransparency?: boolean;
|
||||
readonly legalBasis?: string;
|
||||
readonly matchedRule?: number;
|
||||
readonly positions?: IRectangle[];
|
||||
readonly recommendation?: boolean;
|
||||
readonly redacted?: boolean;
|
||||
readonly reference?: string[];
|
||||
readonly section?: string;
|
||||
readonly sectionNumber?: number;
|
||||
readonly startOffset?: number;
|
||||
readonly textAfter?: string;
|
||||
readonly textBefore?: string;
|
||||
readonly type?: string;
|
||||
readonly value?: string;
|
||||
readonly changeLogType?: 'ADDED' | 'REMOVED' | 'CHANGED';
|
||||
readonly isChangeLogEntry?: boolean;
|
||||
readonly hidden?: boolean;
|
||||
readonly legalBasisList: ILegalBasis[];
|
||||
readonly hintDictionary: boolean;
|
||||
|
||||
reason?: string;
|
||||
|
||||
constructor(
|
||||
redactionLogEntry: IRedactionLogEntry,
|
||||
changeLogType: 'ADDED' | 'REMOVED' | 'CHANGED',
|
||||
isChangeLogEntry: boolean,
|
||||
hidden: boolean,
|
||||
hintDictionary: boolean,
|
||||
legalBasisList: ILegalBasis[],
|
||||
) {
|
||||
this.changes = redactionLogEntry.changes;
|
||||
this.manualChanges = redactionLogEntry.manualChanges;
|
||||
this.color = redactionLogEntry.color;
|
||||
this.comments = redactionLogEntry.comments;
|
||||
this.dictionaryEntry = redactionLogEntry.dictionaryEntry;
|
||||
this.dossierDictionaryEntry = redactionLogEntry.dossierDictionaryEntry;
|
||||
this.endOffset = redactionLogEntry.endOffset;
|
||||
this.engines = redactionLogEntry.engines;
|
||||
this.excluded = redactionLogEntry.excluded;
|
||||
this.hint = redactionLogEntry.hint;
|
||||
this.rectangle = redactionLogEntry.rectangle;
|
||||
this.id = redactionLogEntry.id;
|
||||
this.image = redactionLogEntry.image;
|
||||
this.imageHasTransparency = redactionLogEntry.imageHasTransparency;
|
||||
this.legalBasis = redactionLogEntry.legalBasis;
|
||||
this.matchedRule = redactionLogEntry.matchedRule;
|
||||
this.positions = redactionLogEntry.positions;
|
||||
this.reason = redactionLogEntry.reason;
|
||||
this.recommendation = redactionLogEntry.recommendation;
|
||||
this.redacted = redactionLogEntry.redacted;
|
||||
this.reference = redactionLogEntry.reference;
|
||||
this.section = redactionLogEntry.section;
|
||||
this.sectionNumber = redactionLogEntry.sectionNumber;
|
||||
this.startOffset = redactionLogEntry.startOffset;
|
||||
this.textAfter = redactionLogEntry.textAfter;
|
||||
this.textBefore = redactionLogEntry.textBefore;
|
||||
this.type = redactionLogEntry.type;
|
||||
this.value = redactionLogEntry.value;
|
||||
this.changeLogType = changeLogType;
|
||||
this.isChangeLogEntry = isChangeLogEntry;
|
||||
this.hidden = hidden;
|
||||
this.hintDictionary = hintDictionary;
|
||||
this.legalBasisList = legalBasisList;
|
||||
}
|
||||
}
|
||||
@ -70,6 +70,7 @@
|
||||
<p class="download-includes">{{ 'download-includes' | translate }}</p>
|
||||
<div class="d-flex">
|
||||
<redaction-select
|
||||
[height]="150"
|
||||
[label]="'report-type.label' | translate: { length: reportTemplateIdsLength }"
|
||||
[optionTemplate]="reportTemplateOptionTemplate"
|
||||
[options]="availableReportTypes"
|
||||
@ -78,6 +79,7 @@
|
||||
formControlName="reportTemplateIds"
|
||||
></redaction-select>
|
||||
<redaction-select
|
||||
[height]="150"
|
||||
[label]="'download-type.label' | translate: { length: downloadFileTypesLength }"
|
||||
[options]="downloadTypes"
|
||||
formControlName="downloadFileTypes"
|
||||
|
||||
@ -6,15 +6,9 @@ import { AnnotationActionsService } from '../../services/annotation-actions.serv
|
||||
import { WebViewerInstance } from '@pdftron/webviewer';
|
||||
import { UserService } from '@services/user.service';
|
||||
import { Dossier, File } from '@red/domain';
|
||||
import { defaultDialogConfig, Required } from '@iqser/common-ui';
|
||||
import { Required } from '@iqser/common-ui';
|
||||
import { DossiersService } from '@services/entity-services/dossiers.service';
|
||||
import { MatDialog } from '@angular/material/dialog';
|
||||
import {
|
||||
AcceptRecommendationData,
|
||||
AcceptRecommendationDialogComponent,
|
||||
AcceptRecommendationReturnType,
|
||||
} from '../../dialogs/accept-recommendation-dialog/accept-recommendation-dialog.component';
|
||||
import { filter } from 'rxjs/operators';
|
||||
import { ManualAnnotationService } from '../../../../services/manual-annotation.service';
|
||||
import { AnnotationReferencesService } from '../../services/annotation-references.service';
|
||||
import { MultiSelectService } from '../../services/multi-select.service';
|
||||
@ -107,21 +101,7 @@ export class AnnotationActionsComponent implements OnChanges {
|
||||
}
|
||||
|
||||
acceptRecommendation($event: MouseEvent) {
|
||||
const dialogRef = this._dialog.open<AcceptRecommendationDialogComponent, AcceptRecommendationData, AcceptRecommendationReturnType>(
|
||||
AcceptRecommendationDialogComponent,
|
||||
{ ...defaultDialogConfig, autoFocus: true, data: { annotations: this.annotations, file: this.file } },
|
||||
);
|
||||
const dialogClosed = dialogRef.afterClosed().pipe(filter(value => !!value && !!value.annotations));
|
||||
dialogClosed.subscribe(({ annotations, comment: commentText }) => {
|
||||
const comment = commentText ? { text: commentText } : undefined;
|
||||
this.annotationActionsService.convertRecommendationToAnnotation(
|
||||
$event,
|
||||
annotations,
|
||||
this.file,
|
||||
this.annotationsChanged,
|
||||
comment,
|
||||
);
|
||||
});
|
||||
this.annotationActionsService.convertRecommendationToAnnotation($event, this.annotations, this.file, this.annotationsChanged);
|
||||
}
|
||||
|
||||
hideAnnotation($event: MouseEvent) {
|
||||
|
||||
@ -2,7 +2,7 @@
|
||||
<mat-icon [svgIcon]="'red:redaction-changes'"></mat-icon>
|
||||
</div>
|
||||
|
||||
<ng-container *ngIf="hasEnginesToShow">
|
||||
<ng-container *ngIf="hasEnginesToShow && !isSelected">
|
||||
<div #trigger="cdkOverlayOrigin" (mouseout)="isPopoverOpen = false" (mouseover)="isPopoverOpen = true" cdkOverlayOrigin class="chip">
|
||||
<ng-container *ngFor="let engine of engines">
|
||||
<mat-icon *ngIf="engine.show" [svgIcon]="engine.icon"></mat-icon>
|
||||
|
||||
@ -26,6 +26,7 @@ type EngineName = keyof typeof Engines;
|
||||
})
|
||||
export class AnnotationDetailsComponent implements OnChanges {
|
||||
@Input() annotation: AnnotationWrapper;
|
||||
@Input() isSelected: boolean;
|
||||
|
||||
isPopoverOpen = false;
|
||||
hasEnginesToShow: boolean;
|
||||
|
||||
@ -41,7 +41,7 @@
|
||||
<redaction-comments #comments [annotation]="annotation"></redaction-comments>
|
||||
</div>
|
||||
|
||||
<redaction-annotation-details [annotation]="annotation"></redaction-annotation-details>
|
||||
<redaction-annotation-details [annotation]="annotation" [isSelected]="isSelected(annotation.id)"></redaction-annotation-details>
|
||||
</div>
|
||||
|
||||
<ng-container *ngIf="annotationReferencesService.annotation$ | async as annotation">
|
||||
|
||||
@ -39,6 +39,7 @@ export class AnnotationsListComponent implements OnChanges {
|
||||
}
|
||||
|
||||
annotationClicked(annotation: AnnotationWrapper, $event: MouseEvent): void {
|
||||
console.log(annotation);
|
||||
if (($event?.target as IqserEventTarget)?.localName === 'input') {
|
||||
return;
|
||||
}
|
||||
|
||||
@ -2,8 +2,8 @@ import { ChangeDetectionStrategy, Component, EventEmitter, Input, OnChanges, Out
|
||||
import { File, ViewMode } from '@red/domain';
|
||||
import { ViewModeService } from '../../services/view-mode.service';
|
||||
import { FilePreviewStateService } from '../../services/file-preview-state.service';
|
||||
import { Observable } from 'rxjs';
|
||||
import { filter, switchMap } from 'rxjs/operators';
|
||||
import { combineLatest, Observable } from 'rxjs';
|
||||
import { filter, map, switchMap } from 'rxjs/operators';
|
||||
|
||||
@Component({
|
||||
selector: 'redaction-view-switch [file]',
|
||||
@ -21,7 +21,11 @@ export class ViewSwitchComponent implements OnChanges {
|
||||
constructor(readonly viewModeService: ViewModeService, private readonly _stateService: FilePreviewStateService) {
|
||||
this.canSwitchToDeltaView$ = this._stateService.fileData$.pipe(
|
||||
filter(fileData => !!fileData),
|
||||
switchMap(fileData => fileData?.hasChangeLog$),
|
||||
switchMap(fileData =>
|
||||
combineLatest([fileData.hasChangeLog$, fileData.file$]).pipe(
|
||||
map(([hasChangeLog, file]) => hasChangeLog && !file.isApproved),
|
||||
),
|
||||
),
|
||||
);
|
||||
}
|
||||
|
||||
|
||||
@ -15,6 +15,14 @@ import { toPosition } from '../../../utils/pdf-calculation.utils';
|
||||
import { AnnotationDrawService } from './annotation-draw.service';
|
||||
import { translateQuads } from '@utils/pdf-coordinates';
|
||||
import { DossiersService } from '@services/entity-services/dossiers.service';
|
||||
import {
|
||||
AcceptRecommendationData,
|
||||
AcceptRecommendationDialogComponent,
|
||||
AcceptRecommendationReturnType,
|
||||
} from '../dialogs/accept-recommendation-dialog/accept-recommendation-dialog.component';
|
||||
import { defaultDialogConfig } from '@iqser/common-ui';
|
||||
import { filter } from 'rxjs/operators';
|
||||
import { MatDialog } from '@angular/material/dialog';
|
||||
import Annotation = Core.Annotations.Annotation;
|
||||
|
||||
@Injectable()
|
||||
@ -27,6 +35,7 @@ export class AnnotationActionsService {
|
||||
private readonly _manualAnnotationService: ManualAnnotationService,
|
||||
private readonly _translateService: TranslateService,
|
||||
private readonly _dialogService: DossiersDialogService,
|
||||
private readonly _dialog: MatDialog,
|
||||
private readonly _annotationDrawService: AnnotationDrawService,
|
||||
private readonly _dossiersService: DossiersService,
|
||||
) {}
|
||||
@ -181,19 +190,26 @@ export class AnnotationActionsService {
|
||||
|
||||
convertRecommendationToAnnotation(
|
||||
$event: any,
|
||||
annotations: AnnotationWrapper[],
|
||||
recommendations: AnnotationWrapper[],
|
||||
file: File,
|
||||
annotationsChanged: EventEmitter<AnnotationWrapper>,
|
||||
comment?: { text: string },
|
||||
) {
|
||||
$event?.stopPropagation();
|
||||
|
||||
annotations.forEach(annotation => {
|
||||
this._processObsAndEmit(
|
||||
this._manualAnnotationService.addRecommendation(annotation, file, comment),
|
||||
annotation,
|
||||
annotationsChanged,
|
||||
);
|
||||
const dialogRef = this._dialog.open<AcceptRecommendationDialogComponent, AcceptRecommendationData, AcceptRecommendationReturnType>(
|
||||
AcceptRecommendationDialogComponent,
|
||||
{ ...defaultDialogConfig, autoFocus: true, data: { annotations: recommendations, file } },
|
||||
);
|
||||
const dialogClosed = dialogRef.afterClosed().pipe(filter(value => !!value && !!value.annotations));
|
||||
dialogClosed.subscribe(({ annotations, comment: commentText }) => {
|
||||
const comment = commentText ? { text: commentText } : undefined;
|
||||
annotations.forEach(annotation => {
|
||||
this._processObsAndEmit(
|
||||
this._manualAnnotationService.addRecommendation(annotation, file, comment),
|
||||
annotation,
|
||||
annotationsChanged,
|
||||
);
|
||||
});
|
||||
});
|
||||
}
|
||||
|
||||
|
||||
@ -147,7 +147,7 @@ export class AnnotationDrawService {
|
||||
const dossierTemplateId = this._dossiersService.find(dossierId).dossierTemplateId;
|
||||
|
||||
let annotation;
|
||||
if (annotationWrapper.value === 'Rectangle' || annotationWrapper.isImage) {
|
||||
if (annotationWrapper.rectangle || annotationWrapper.isImage) {
|
||||
annotation = new activeViewer.Core.Annotations.RectangleAnnotation();
|
||||
const pageHeight = activeViewer.Core.documentViewer.getPageHeight(pageNumber);
|
||||
const firstPosition = annotationWrapper.positions[0];
|
||||
|
||||
@ -20,6 +20,7 @@ import { annotationActionsTranslations } from '../translations/annotation-action
|
||||
import { marker as _ } from '@biesbjerg/ngx-translate-extract-marker';
|
||||
import { HttpErrorResponse, HttpStatusCode } from '@angular/common/http';
|
||||
import { DossiersService } from '@services/entity-services/dossiers.service';
|
||||
import { Observable } from 'rxjs';
|
||||
|
||||
@Injectable()
|
||||
export class ManualAnnotationService extends GenericService<IManualAddResponse> {
|
||||
@ -54,7 +55,13 @@ export class ManualAnnotationService extends GenericService<IManualAddResponse>
|
||||
};
|
||||
}
|
||||
|
||||
_makeRequest(mode: AnnotationActionMode, file: File, body: any, secondParam: any = null, modifyDictionary = false) {
|
||||
_makeRequest(
|
||||
mode: AnnotationActionMode,
|
||||
file: File,
|
||||
body: any,
|
||||
secondParam: any = null,
|
||||
modifyDictionary = false,
|
||||
): Observable<unknown> {
|
||||
const obs = !secondParam
|
||||
? this[this.CONFIG[mode]](body, file.dossierId, file.id)
|
||||
: this[this.CONFIG[mode]](body, secondParam, file.dossierId, file.id);
|
||||
@ -186,18 +193,13 @@ export class ManualAnnotationService extends GenericService<IManualAddResponse>
|
||||
|
||||
if (this._permissionsService.isApprover(this._dossier(file))) {
|
||||
// if it was something manual simply decline the existing request
|
||||
if (annotationWrapper.type === 'manual') {
|
||||
mode = 'undo';
|
||||
body = annotationWrapper.id;
|
||||
} else {
|
||||
mode = 'remove';
|
||||
body = {
|
||||
annotationId: annotationWrapper.id,
|
||||
removeFromDictionary,
|
||||
comment: comment,
|
||||
};
|
||||
removeDict = removeFromDictionary;
|
||||
}
|
||||
mode = 'remove';
|
||||
body = {
|
||||
annotationId: annotationWrapper.id,
|
||||
removeFromDictionary,
|
||||
comment: comment,
|
||||
};
|
||||
removeDict = removeFromDictionary;
|
||||
} else {
|
||||
mode = 'request-remove';
|
||||
body = {
|
||||
|
||||
@ -11,6 +11,21 @@
|
||||
}
|
||||
}
|
||||
|
||||
// https://stackoverflow.com/questions/34641281/how-to-add-class-to-host-element
|
||||
:host(.fixed-height) {
|
||||
height: var(--height);
|
||||
overflow: hidden;
|
||||
|
||||
::ng-deep .mat-chip-list {
|
||||
height: calc(var(--height) - 44px);
|
||||
|
||||
.mat-chip-list-wrapper {
|
||||
height: 100%;
|
||||
overflow: scroll;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
.label-header {
|
||||
padding: 16px 16px 14px 16px;
|
||||
display: flex;
|
||||
|
||||
@ -1,4 +1,4 @@
|
||||
import { AfterViewInit, ChangeDetectorRef, Component, Input, TemplateRef, ViewChild } from '@angular/core';
|
||||
import { AfterViewInit, ChangeDetectorRef, Component, ElementRef, HostBinding, Input, TemplateRef, ViewChild } from '@angular/core';
|
||||
import { ControlValueAccessor, NG_VALUE_ACCESSOR } from '@angular/forms';
|
||||
import { MatChip, MatChipList, MatChipSelectionChange } from '@angular/material/chips';
|
||||
|
||||
@ -20,13 +20,27 @@ export class SelectComponent implements AfterViewInit, ControlValueAccessor {
|
||||
@Input() options: any[];
|
||||
@Input() disabled = false;
|
||||
@Input() multiple = true;
|
||||
|
||||
@ViewChild(MatChipList) chipList: MatChipList;
|
||||
|
||||
private _value: any[] = [];
|
||||
private _onChange: (value: any[]) => void;
|
||||
|
||||
constructor(private readonly _changeDetector: ChangeDetectorRef) {}
|
||||
constructor(private readonly _changeDetector: ChangeDetectorRef, private readonly _elementRef: ElementRef) {}
|
||||
|
||||
@HostBinding('class.fixed-height')
|
||||
get isFixedHeight(): boolean {
|
||||
return !!this._height;
|
||||
}
|
||||
|
||||
private _height?: number;
|
||||
|
||||
@Input()
|
||||
set height(value: number) {
|
||||
this._height = value;
|
||||
if (this.isFixedHeight) {
|
||||
const nativeElement = this._elementRef.nativeElement as HTMLElement;
|
||||
nativeElement.style.setProperty('--height', `${this._height}px`);
|
||||
}
|
||||
}
|
||||
|
||||
@Input() valueMapper: (option: any) => any = option => option.key;
|
||||
|
||||
|
||||
@ -6,6 +6,7 @@ export const annotationChangesTranslations: { [key in KeysOf<AnnotationWrapper>]
|
||||
hasBeenResized: _('annotation-changes.resized'),
|
||||
hasBeenRecategorized: _('annotation-changes.recategorized'),
|
||||
hasLegalBasisChanged: _('annotation-changes.legal-basis'),
|
||||
hasBeenForced: _('annotation-changes.forced'),
|
||||
hasBeenForcedRedaction: _('annotation-changes.forced-redaction'),
|
||||
hasBeenForcedHint: _('annotation-changes.forced-hint'),
|
||||
hasBeenRemovedByManualOverride: _('annotation-changes.removed-manual'),
|
||||
} as const;
|
||||
|
||||
@ -14,6 +14,7 @@ export const annotationTypesTranslations: { [key in AnnotationSuperType]: string
|
||||
'suggestion-change-legal-basis': _('annotation-type.suggestion-change-legal-basis'),
|
||||
'suggestion-recategorize-image': _('annotation-type.suggestion-recategorize-image'),
|
||||
'suggestion-force-redaction': _('annotation-type.suggestion-force-redaction'),
|
||||
'suggestion-force-hint': _('annotation-type.suggestion-force-hint'),
|
||||
'suggestion-remove': _('annotation-type.suggestion-remove'),
|
||||
'suggestion-remove-dictionary': _('annotation-type.suggestion-remove-dictionary'),
|
||||
'suggestion-resize': _('annotation-type.suggestion-resize'),
|
||||
|
||||
@ -3,7 +3,8 @@ import { AnnotationSuperType } from '../../models/file/annotation.wrapper';
|
||||
export const SuperTypeSorter: { [key in AnnotationSuperType]: number } = {
|
||||
'suggestion-change-legal-basis': 14,
|
||||
'suggestion-force-redaction': 15,
|
||||
'suggestion-recategorize-image': 16,
|
||||
'suggestion-force-hint': 16,
|
||||
'suggestion-recategorize-image': 17,
|
||||
'suggestion-resize': 18,
|
||||
recommendation: 7,
|
||||
'suggestion-add-dictionary': 12,
|
||||
@ -14,6 +15,6 @@ export const SuperTypeSorter: { [key in AnnotationSuperType]: number } = {
|
||||
skipped: 50,
|
||||
redaction: 1,
|
||||
'manual-redaction': 2,
|
||||
hint: 17,
|
||||
hint: 19,
|
||||
'declined-suggestion': 20,
|
||||
};
|
||||
|
||||
@ -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://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-04.iqser.cloud/auth/realms/redaction",
|
||||
"OAUTH_URL": "https://dev-05.iqser.cloud/auth/realms/redaction",
|
||||
"RECENT_PERIOD_IN_HOURS": 24,
|
||||
"SELECTION_MODE": "structural"
|
||||
}
|
||||
|
||||
@ -272,7 +272,8 @@
|
||||
"undo": "Undo"
|
||||
},
|
||||
"annotation-changes": {
|
||||
"forced": "Redaction forced",
|
||||
"forced-redaction": "Redaction forced",
|
||||
"forced-hint": "Hint forced",
|
||||
"header": "Manual changes:",
|
||||
"legal-basis": "Reason changed",
|
||||
"recategorized": "Image category changed",
|
||||
@ -296,8 +297,9 @@
|
||||
"suggestion-add-dictionary": "Suggested dictionary add",
|
||||
"suggestion-change-legal-basis": "Suggested change legal basis",
|
||||
"suggestion-force-redaction": "Suggestion force redaction",
|
||||
"suggestion-force-redaction": "Suggestion force hint",
|
||||
"suggestion-recategorize-image": "Suggested recategorize image",
|
||||
"suggestion-remove": "Suggested hint removal",
|
||||
"suggestion-remove": "Suggested local removal",
|
||||
"suggestion-remove-dictionary": "Suggested dictionary removal",
|
||||
"suggestion-resize": "Suggested Resize"
|
||||
},
|
||||
|
||||
@ -4,9 +4,8 @@ export interface IChange {
|
||||
type?: ChangeType;
|
||||
}
|
||||
|
||||
export const ChangeTypes = {
|
||||
ADDED: 'ADDED',
|
||||
CHANGED: 'CHANGED',
|
||||
REMOVED: 'REMOVED',
|
||||
} as const;
|
||||
export type ChangeType = keyof typeof ChangeTypes;
|
||||
export enum ChangeType {
|
||||
ADDED = 'ADDED',
|
||||
CHANGED = 'CHANGED',
|
||||
REMOVED = 'REMOVED',
|
||||
}
|
||||
|
||||
@ -10,3 +10,4 @@ export * from './manual-add.response';
|
||||
export * from './approve-request';
|
||||
export * from './image-recategorization.request';
|
||||
export * from './resize.request';
|
||||
export * from './manual-change';
|
||||
|
||||
11
libs/red-domain/src/lib/redaction-log/manual-change.ts
Normal file
11
libs/red-domain/src/lib/redaction-log/manual-change.ts
Normal file
@ -0,0 +1,11 @@
|
||||
import { LogEntryStatus, ManualRedactionType } from './types';
|
||||
|
||||
export interface IManualChange {
|
||||
userId: string;
|
||||
processedDate: string;
|
||||
annotationStatus: LogEntryStatus;
|
||||
manualRedactionType: ManualRedactionType;
|
||||
propertyChanges: { [key: string]: any };
|
||||
|
||||
processed: boolean;
|
||||
}
|
||||
@ -1,17 +1,18 @@
|
||||
import { IChange } from './change';
|
||||
import { IComment } from './comment';
|
||||
import { IRectangle } from '../geometry';
|
||||
import { LogEntryEngine, LogEntryStatus, ManualRedactionType } from './types';
|
||||
import { List } from '@iqser/common-ui';
|
||||
import { LogEntryEngine } from './types';
|
||||
import { IManualChange } from './manual-change';
|
||||
|
||||
export interface IRedactionLogEntry {
|
||||
changes?: IChange[];
|
||||
color?: List<number>;
|
||||
comments?: List<IComment>;
|
||||
manualChanges?: IManualChange[];
|
||||
color?: number[];
|
||||
comments?: IComment[];
|
||||
dictionaryEntry?: boolean;
|
||||
dossierDictionaryEntry?: boolean;
|
||||
endOffset?: number;
|
||||
engines?: List<LogEntryEngine>;
|
||||
engines?: LogEntryEngine[];
|
||||
excluded?: boolean;
|
||||
hint?: boolean;
|
||||
rectangle?: boolean;
|
||||
@ -19,21 +20,15 @@ export interface IRedactionLogEntry {
|
||||
image?: boolean;
|
||||
imageHasTransparency?: boolean;
|
||||
legalBasis?: string;
|
||||
legalBasisChangeValue?: string;
|
||||
manual?: boolean;
|
||||
manualRedactionType?: ManualRedactionType;
|
||||
manualRedactionUserId?: string;
|
||||
matchedRule?: number;
|
||||
positions?: List<IRectangle>;
|
||||
positions?: IRectangle[];
|
||||
reason?: string;
|
||||
recategorizationType?: string;
|
||||
recommendation?: boolean;
|
||||
redacted?: boolean;
|
||||
reference?: List;
|
||||
reference?: string[];
|
||||
section?: string;
|
||||
sectionNumber?: number;
|
||||
startOffset?: number;
|
||||
status?: LogEntryStatus;
|
||||
textAfter?: string;
|
||||
textBefore?: string;
|
||||
type?: string;
|
||||
|
||||
@ -1,23 +1,23 @@
|
||||
export const LogEntryEngines = {
|
||||
DICTIONARY: 'DICTIONARY',
|
||||
NER: 'NER',
|
||||
RULE: 'RULE',
|
||||
};
|
||||
export type LogEntryEngine = keyof typeof LogEntryEngines;
|
||||
export enum LogEntryEngine {
|
||||
DICTIONARY = 'DICTIONARY',
|
||||
NER = 'NER',
|
||||
RULE = 'RULE',
|
||||
}
|
||||
|
||||
export const ManualRedactionTypes = {
|
||||
ADD: 'ADD',
|
||||
FORCE_REDACT: 'FORCE_REDACT',
|
||||
LEGAL_BASIS_CHANGE: 'LEGAL_BASIS_CHANGE',
|
||||
RECATEGORIZE: 'RECATEGORIZE',
|
||||
REMOVE: 'REMOVE',
|
||||
} as const;
|
||||
export type ManualRedactionType = keyof typeof ManualRedactionTypes;
|
||||
export enum ManualRedactionType {
|
||||
ADD_LOCALLY = 'ADD_LOCALLY',
|
||||
ADD_TO_DICTIONARY = 'ADD_TO_DICTIONARY',
|
||||
REMOVE_LOCALLY = 'REMOVE_LOCALLY',
|
||||
REMOVE_FROM_DICTIONARY = 'REMOVE_FROM_DICTIONARY',
|
||||
FORCE_REDACT = 'FORCE_REDACT',
|
||||
FORCE_HINT = 'FORCE_HINT',
|
||||
RECATEGORIZE = 'RECATEGORIZE',
|
||||
LEGAL_BASIS_CHANGE = 'LEGAL_BASIS_CHANGE',
|
||||
RESIZE = 'RESIZE',
|
||||
}
|
||||
|
||||
export const LogEntryStatuses = {
|
||||
APPROVED: 'APPROVED',
|
||||
DECLINED: 'DECLINED',
|
||||
REQUESTED: 'REQUESTED',
|
||||
} as const;
|
||||
|
||||
export type LogEntryStatus = keyof typeof LogEntryStatuses;
|
||||
export enum LogEntryStatus {
|
||||
APPROVED = 'APPROVED',
|
||||
DECLINED = 'DECLINED',
|
||||
REQUESTED = 'REQUESTED',
|
||||
}
|
||||
|
||||
@ -1,6 +1,6 @@
|
||||
{
|
||||
"name": "redaction",
|
||||
"version": "3.193.0",
|
||||
"version": "3.196.0",
|
||||
"private": true,
|
||||
"license": "MIT",
|
||||
"scripts": {
|
||||
|
||||
Binary file not shown.
Loading…
x
Reference in New Issue
Block a user