reworked analysis required

This commit is contained in:
Timo Bejan 2020-11-09 23:47:29 +02:00
parent 5f16c2988e
commit 1f2acf201e
12 changed files with 71 additions and 34 deletions

View File

@ -1,8 +1,10 @@
import { FilterModel } from '../model/filter.model'; import { FilterModel } from '../model/filter.model';
import { FileStatusWrapper } from '../../../screens/file/model/file-status.wrapper'; import { FileStatusWrapper } from '../../../screens/file/model/file-status.wrapper';
import { ProjectWrapper } from '../../../state/model/project.wrapper'; import { ProjectWrapper } from '../../../state/model/project.wrapper';
import { PermissionsService } from '../../service/permissions.service';
export const RedactionFilterSorter = { export const RedactionFilterSorter = {
analysis: 0,
hint: 1, hint: 1,
redaction: 2, redaction: 2,
suggestion: 3, suggestion: 3,
@ -22,9 +24,16 @@ export function handleCheckedValue(filter: FilterModel) {
} }
} }
export function checkFilter(entity: any, filters: FilterModel[], validate: Function, matchAll: boolean = false) { export function checkFilter(entity: any, filters: FilterModel[], validate: Function, validateArgs: any = [], matchAll: boolean = false) {
const hasChecked = filters.find((f) => f.checked); const hasChecked = filters.find((f) => f.checked);
if (validateArgs) {
if (!Array.isArray(validateArgs)) {
validateArgs = [validateArgs];
}
} else {
validateArgs = [];
}
if (!hasChecked) { if (!hasChecked) {
return true; return true;
} }
@ -33,9 +42,9 @@ export function checkFilter(entity: any, filters: FilterModel[], validate: Funct
for (const filter of filters) { for (const filter of filters) {
if (filter.checked) { if (filter.checked) {
if (matchAll) { if (matchAll) {
filterMatched = filterMatched && validate(entity, filter); filterMatched = filterMatched && validate(entity, filter, ...validateArgs);
} else { } else {
filterMatched = filterMatched || validate(entity, filter); filterMatched = filterMatched || validate(entity, filter, ...validateArgs);
} }
} }
} }
@ -45,19 +54,26 @@ export function checkFilter(entity: any, filters: FilterModel[], validate: Funct
export const keyChecker = (key: string) => (entity: any, filter: FilterModel) => entity[key] === filter.key; export const keyChecker = (key: string) => (entity: any, filter: FilterModel) => entity[key] === filter.key;
export const annotationFilterChecker = (f: FileStatusWrapper, filter: FilterModel) => { export const annotationFilterChecker = (input: FileStatusWrapper | ProjectWrapper, filter: FilterModel, permissionsService: PermissionsService) => {
switch (filter.key) { switch (filter.key) {
case 'analysis': {
if (input instanceof ProjectWrapper) {
return permissionsService.projectReanalysisRequired(input);
} else {
return permissionsService.fileRequiresReanalysis(input);
}
}
case 'suggestion': { case 'suggestion': {
return f.hasRequests; return input.hasRequests;
} }
case 'redaction': { case 'redaction': {
return f.hasRedactions; return input.hasRedactions;
} }
case 'hint': { case 'hint': {
return f.hintsOnly; return input.hintsOnly;
} }
case 'none': { case 'none': {
return f.hasNone; return input.hasNone;
} }
} }
}; };
@ -72,12 +88,12 @@ export const dueDateChecker = (pw: ProjectWrapper, filter: FilterModel) => pw.du
export const addedDateChecker = (pw: ProjectWrapper, filter: FilterModel) => pw.addedDateMatches(filter.key); export const addedDateChecker = (pw: ProjectWrapper, filter: FilterModel) => pw.addedDateMatches(filter.key);
export function getFilteredEntities(entities: any[], filters: { values: FilterModel[]; checker: Function; matchAll?: boolean }[]) { export function getFilteredEntities(entities: any[], filters: { values: FilterModel[]; checker: Function; matchAll?: boolean; checkerArgs?: any }[]) {
const filteredEntities = []; const filteredEntities = [];
for (const entity of entities) { for (const entity of entities) {
let add = true; let add = true;
for (const filter of filters) { for (const filter of filters) {
add = add && checkFilter(entity, filter.values, filter.checker, filter.matchAll); add = add && checkFilter(entity, filter.values, filter.checker, filter.checkerArgs, filter.matchAll);
} }
if (add) { if (add) {
filteredEntities.push(entity); filteredEntities.push(entity);

View File

@ -3,6 +3,7 @@ import { AppStateService } from '../../state/app-state.service';
import { UserService, UserWrapper } from '../../user/user.service'; import { UserService, UserWrapper } from '../../user/user.service';
import { FileStatusWrapper } from '../../screens/file/model/file-status.wrapper'; import { FileStatusWrapper } from '../../screens/file/model/file-status.wrapper';
import { Project, User } from '@redaction/red-ui-http'; import { Project, User } from '@redaction/red-ui-http';
import { ProjectWrapper } from '../../state/model/project.wrapper';
@Injectable({ @Injectable({
providedIn: 'root' providedIn: 'root'
@ -22,6 +23,15 @@ export class PermissionsService {
return this.isFileReviewer(fileStatus) || this.isManagerAndOwner(); return this.isFileReviewer(fileStatus) || this.isManagerAndOwner();
} }
projectReanalysisRequired(project?: ProjectWrapper) {
for (let file of project.files) {
const fileReanalysisRequired = this.fileRequiresReanalysis(file);
if (fileReanalysisRequired) {
return true;
}
}
}
fileRequiresReanalysis(fileStatus?: FileStatusWrapper) { fileRequiresReanalysis(fileStatus?: FileStatusWrapper) {
if (!fileStatus) { if (!fileStatus) {
fileStatus = this._appStateService.activeFile; fileStatus = this._appStateService.activeFile;

View File

@ -1,4 +1,5 @@
<div class="needs-work"> <div class="needs-work">
<redaction-annotation-icon *ngIf="reanalysisRequired()" [typeValue]="appStateService.getDictionaryTypeValue('analysis')"></redaction-annotation-icon>
<redaction-annotation-icon <redaction-annotation-icon
*ngIf="needsWorkInput.hasRedactions" *ngIf="needsWorkInput.hasRedactions"
[typeValue]="appStateService.getDictionaryTypeValue('redaction')" [typeValue]="appStateService.getDictionaryTypeValue('redaction')"

View File

@ -1,11 +1,8 @@
import { Component, Input, OnInit } from '@angular/core'; import { Component, Input, OnInit } from '@angular/core';
import { AppStateService } from '../../../state/app-state.service'; import { AppStateService } from '../../../state/app-state.service';
import { PermissionsService } from '../../../common/service/permissions.service';
export interface NeedsWorkInput { import { FileStatusWrapper } from '../../file/model/file-status.wrapper';
hintsOnly?: boolean; import { ProjectWrapper } from '../../../state/model/project.wrapper';
hasRedactions?: boolean;
hasRequests?: boolean;
}
@Component({ @Component({
selector: 'redaction-needs-work-badge', selector: 'redaction-needs-work-badge',
@ -13,9 +10,17 @@ export interface NeedsWorkInput {
styleUrls: ['./needs-work-badge.component.scss'] styleUrls: ['./needs-work-badge.component.scss']
}) })
export class NeedsWorkBadgeComponent implements OnInit { export class NeedsWorkBadgeComponent implements OnInit {
@Input() needsWorkInput: NeedsWorkInput; @Input() needsWorkInput: FileStatusWrapper | ProjectWrapper;
constructor(public appStateService: AppStateService) {} constructor(public appStateService: AppStateService, public permissionsService: PermissionsService) {}
ngOnInit(): void {} ngOnInit(): void {}
reanalysisRequired() {
if (this.needsWorkInput instanceof ProjectWrapper) {
return this.permissionsService.projectReanalysisRequired(this.needsWorkInput);
} else {
return this.permissionsService.fileRequiresReanalysis(this.needsWorkInput);
}
}
} }

View File

@ -12,7 +12,7 @@
</div> </div>
<div class="flex-1 filename page-title"> <div class="flex-1 filename page-title">
<span *ngIf="permissionsService.fileRequiresReanalysis()" class="pill" translate="project-overview.new-rule.label"></span> <!-- <span *ngIf="permissionsService.fileRequiresReanalysis()" class="pill" translate="project-overview.new-rule.label"></span>-->
<span *ngIf="!permissionsService.canPerformAnnotationActions()" class="pill" translate="readonly-pill"></span>&nbsp;<span>{{ <span *ngIf="!permissionsService.canPerformAnnotationActions()" class="pill" translate="readonly-pill"></span>&nbsp;<span>{{
appStateService.activeFile.filename appStateService.activeFile.filename
}}</span> }}</span>

View File

@ -167,10 +167,11 @@ export class ProjectListingScreenComponent implements OnInit {
// Needs work // Needs work
entry.files.forEach((file) => { entry.files.forEach((file) => {
if (file.hintsOnly) allDistinctNeedsWork.add('hint'); if (this.permissionsService.fileRequiresReanalysis(file)) allDistinctNeedsWork.add('analysis');
if (file.hasRedactions) allDistinctNeedsWork.add('redaction'); if (entry.hintsOnly) allDistinctNeedsWork.add('hint');
if (file.hasRequests) allDistinctNeedsWork.add('suggestion'); if (entry.hasRedactions) allDistinctNeedsWork.add('redaction');
if (file.hasNone) allDistinctNeedsWork.add('none'); if (entry.hasRequests) allDistinctNeedsWork.add('suggestion');
if (entry.hasNone) allDistinctNeedsWork.add('none');
}); });
}); });
@ -226,7 +227,7 @@ export class ProjectListingScreenComponent implements OnInit {
{ values: this.statusFilters, checker: projectStatusChecker }, { values: this.statusFilters, checker: projectStatusChecker },
{ values: this.peopleFilters, checker: projectMemberChecker }, { values: this.peopleFilters, checker: projectMemberChecker },
{ values: this.dueDateFilters, checker: dueDateChecker }, { values: this.dueDateFilters, checker: dueDateChecker },
{ values: this.needsWorkFilters, checker: annotationFilterChecker, matchAll: true } { values: this.needsWorkFilters, checker: annotationFilterChecker, matchAll: true, checkerArgs: this.permissionsService }
]; ];
this.detailsContainerFilters = { this.detailsContainerFilters = {
statusFilters: this.statusFilters.map((f) => ({ ...f })) statusFilters: this.statusFilters.map((f) => ({ ...f }))

View File

@ -53,7 +53,7 @@
<div class="mt-24 legend" *ngIf="hasFiles"> <div class="mt-24 legend" *ngIf="hasFiles">
<div *ngFor="let filter of filters.needsWorkFilters" [class.active]="filter.checked" (click)="toggleFilter('needsWorkFilters', filter.key)"> <div *ngFor="let filter of filters.needsWorkFilters" [class.active]="filter.checked" (click)="toggleFilter('needsWorkFilters', filter.key)">
<redaction-annotation-icon *ngIf="filter.key !== 'none'" [typeValue]="appStateService.getDictionaryTypeValue(filter.key)"></redaction-annotation-icon> <redaction-annotation-icon *ngIf="filter.key !== 'none'" [typeValue]="appStateService.getDictionaryTypeValue(filter.key)"></redaction-annotation-icon>
{{ 'project-overview.legend.' + filter.key | translate }} {{ 'filter.' + filter.key | translate }}
</div> </div>
</div> </div>

View File

@ -122,7 +122,7 @@
<div [class.disabled]="fileStatus.isPending || fileStatus.isProcessing" [class.error]="fileStatus.isError" class="table-item-title"> <div [class.disabled]="fileStatus.isPending || fileStatus.isProcessing" [class.error]="fileStatus.isError" class="table-item-title">
{{ fileStatus.filename }} {{ fileStatus.filename }}
</div> </div>
<span *ngIf="permissionsService.fileRequiresReanalysis(fileStatus)" class="pill" translate="project-overview.new-rule.label"></span> <!-- <span *ngIf="permissionsService.fileRequiresReanalysis(fileStatus)" class="pill" translate="project-overview.new-rule.label"></span>-->
</div> </div>
</div> </div>

View File

@ -191,6 +191,7 @@ export class ProjectOverviewScreenComponent implements OnInit, OnDestroy {
// Needs work // Needs work
this.appStateService.activeProject.files.forEach((file) => { this.appStateService.activeProject.files.forEach((file) => {
if (this.permissionsService.fileRequiresReanalysis(file)) allDistinctNeedsWork.add('analysis');
if (file.hintsOnly) allDistinctNeedsWork.add('hint'); if (file.hintsOnly) allDistinctNeedsWork.add('hint');
if (file.hasRedactions) allDistinctNeedsWork.add('redaction'); if (file.hasRedactions) allDistinctNeedsWork.add('redaction');
if (file.hasRequests) allDistinctNeedsWork.add('suggestion'); if (file.hasRequests) allDistinctNeedsWork.add('suggestion');
@ -250,7 +251,7 @@ export class ProjectOverviewScreenComponent implements OnInit, OnDestroy {
const filters = [ const filters = [
{ values: this.statusFilters, checker: keyChecker('status') }, { values: this.statusFilters, checker: keyChecker('status') },
{ values: this.peopleFilters, checker: keyChecker('currentReviewer') }, { values: this.peopleFilters, checker: keyChecker('currentReviewer') },
{ values: this.needsWorkFilters, checker: annotationFilterChecker, matchAll: true } { values: this.needsWorkFilters, checker: annotationFilterChecker, matchAll: true, checkerArgs: this.permissionsService }
]; ];
this.displayedFiles = getFilteredEntities(this.appStateService.activeProject.files, filters); this.displayedFiles = getFilteredEntities(this.appStateService.activeProject.files, filters);
this.detailsContainerFilters = { this.detailsContainerFilters = {

View File

@ -390,6 +390,11 @@ export class AppStateService {
type: 'add', type: 'add',
virtual: true virtual: true
}; };
this._dictionaryData['analysis'] = {
hexColor: '#dd4d50',
type: 'analysis',
virtual: true
};
}) })
); );

View File

@ -8,6 +8,7 @@ export class ProjectWrapper {
hintsOnly?: boolean; hintsOnly?: boolean;
hasRedactions?: boolean; hasRedactions?: boolean;
hasRequests?: boolean; hasRequests?: boolean;
hasNone?: boolean;
allFilesApproved?: boolean; allFilesApproved?: boolean;
@ -63,6 +64,7 @@ export class ProjectWrapper {
this.hintsOnly = false; this.hintsOnly = false;
this.hasRedactions = false; this.hasRedactions = false;
this.hasRequests = false; this.hasRequests = false;
this.hasNone = false;
this.allFilesApproved = true; this.allFilesApproved = true;
this._files.forEach((f) => { this._files.forEach((f) => {
this.hintsOnly = this.hintsOnly || f.hintsOnly; this.hintsOnly = this.hintsOnly || f.hintsOnly;
@ -70,5 +72,6 @@ export class ProjectWrapper {
this.hasRequests = this.hasRequests || f.hasRequests; this.hasRequests = this.hasRequests || f.hasRequests;
this.allFilesApproved = this.allFilesApproved && f.isApproved; this.allFilesApproved = this.allFilesApproved && f.isApproved;
}); });
this.hasNone = !this.hasRequests && !this.hasRedactions && !this.hintsOnly;
} }
} }

View File

@ -239,13 +239,7 @@
}, },
"header": "Project Overview", "header": "Project Overview",
"upload-document": "Upload Document", "upload-document": "Upload Document",
"no-project": "Requested project: {{projectId}} does not exist! <a href='/ui/projects'>Back to Project Listing. <a/>", "no-project": "Requested project: {{projectId}} does not exist! <a href='/ui/projects'>Back to Project Listing. <a/>"
"legend": {
"hint": "Hints only",
"redaction": "Redacted",
"suggestion": "Suggested Redaction",
"none": "No Annotations"
}
}, },
"file-preview": { "file-preview": {
"show-redacted-view": "Show Redacted Preview", "show-redacted-view": "Show Redacted Preview",
@ -317,6 +311,7 @@
"hint": "Hints only", "hint": "Hints only",
"redaction": "Redacted", "redaction": "Redacted",
"suggestion": "Suggested Redaction", "suggestion": "Suggested Redaction",
"analysis": "Re-analysis required",
"none": "No Annotations" "none": "No Annotations"
}, },
"annotation-filter": { "annotation-filter": {