Merge pull request #343 from AnalogJ/app_db_settings
adding tests. Make sure that device status depends on the configured threshold
This commit is contained in:
+6
-6
@@ -1,15 +1,15 @@
|
|||||||
<div [ngClass]="{ 'border-green': deviceStatusString(deviceSummary) == 'passed',
|
<div [ngClass]="{ 'border-green': deviceStatusForModelWithThreshold(deviceSummary.device, !!deviceSummary.smart, config.metrics.status_threshold) == 'passed',
|
||||||
'border-red': deviceStatusString(deviceSummary) == 'failed' }"
|
'border-red': deviceStatusForModelWithThreshold(deviceSummary.device, !!deviceSummary.smart, config.metrics.status_threshold) == 'failed' }"
|
||||||
class="relative flex flex-col flex-auto p-6 pr-3 pb-3 bg-card rounded border-l-4 shadow-md overflow-hidden">
|
class="relative flex flex-col flex-auto p-6 pr-3 pb-3 bg-card rounded border-l-4 shadow-md overflow-hidden">
|
||||||
<div class="absolute bottom-0 right-0 w-24 h-24 -m-6">
|
<div class="absolute bottom-0 right-0 w-24 h-24 -m-6">
|
||||||
<mat-icon class="icon-size-96 opacity-12 text-green"
|
<mat-icon class="icon-size-96 opacity-12 text-green"
|
||||||
*ngIf="deviceStatusString(deviceSummary) == 'passed'"
|
*ngIf="deviceStatusForModelWithThreshold(deviceSummary.device, !!deviceSummary.smart, config.metrics.status_threshold) == 'passed'"
|
||||||
[svgIcon]="'heroicons_outline:check-circle'"></mat-icon>
|
[svgIcon]="'heroicons_outline:check-circle'"></mat-icon>
|
||||||
<mat-icon class="icon-size-96 opacity-12 text-red"
|
<mat-icon class="icon-size-96 opacity-12 text-red"
|
||||||
*ngIf="deviceStatusString(deviceSummary) == 'failed'"
|
*ngIf="deviceStatusForModelWithThreshold(deviceSummary.device, !!deviceSummary.smart, config.metrics.status_threshold) == 'failed'"
|
||||||
[svgIcon]="'heroicons_outline:exclamation-circle'"></mat-icon>
|
[svgIcon]="'heroicons_outline:exclamation-circle'"></mat-icon>
|
||||||
<mat-icon class="icon-size-96 opacity-12 text-yellow"
|
<mat-icon class="icon-size-96 opacity-12 text-yellow"
|
||||||
*ngIf="deviceStatusString(deviceSummary) == 'unknown'"
|
*ngIf="deviceStatusForModelWithThreshold(deviceSummary.device, !!deviceSummary.smart, config.metrics.status_threshold) == 'unknown'"
|
||||||
[svgIcon]="'heroicons_outline:question-mark-circle'"></mat-icon>
|
[svgIcon]="'heroicons_outline:question-mark-circle'"></mat-icon>
|
||||||
</div>
|
</div>
|
||||||
<div class="flex items-center">
|
<div class="flex items-center">
|
||||||
@@ -47,7 +47,7 @@
|
|||||||
<div class="flex flex-col mx-6 my-3 xs:w-full">
|
<div class="flex flex-col mx-6 my-3 xs:w-full">
|
||||||
<div class="font-semibold text-xs text-hint uppercase tracking-wider leading-none">Status</div>
|
<div class="font-semibold text-xs text-hint uppercase tracking-wider leading-none">Status</div>
|
||||||
<div class="mt-2 font-medium text-3xl leading-none"
|
<div class="mt-2 font-medium text-3xl leading-none"
|
||||||
*ngIf="deviceSummary.smart?.collector_date; else unknownStatus">{{ deviceStatusString(deviceSummary) | titlecase}}</div>
|
*ngIf="deviceSummary.smart?.collector_date; else unknownStatus">{{ deviceStatusForModelWithThreshold(deviceSummary.device, !!deviceSummary.smart, config.metrics.status_threshold) | titlecase}}</div>
|
||||||
<ng-template #unknownStatus><div class="mt-2 font-medium text-3xl leading-none">No Data</div></ng-template>
|
<ng-template #unknownStatus><div class="mt-2 font-medium text-3xl leading-none">No Data</div></ng-template>
|
||||||
</div>
|
</div>
|
||||||
<div class="flex flex-col mx-6 my-3 xs:w-full">
|
<div class="flex flex-col mx-6 my-3 xs:w-full">
|
||||||
|
|||||||
+61
-10
@@ -10,6 +10,10 @@ import {TREO_APP_CONFIG} from '@treo/services/config/config.constants';
|
|||||||
import {DeviceSummaryModel} from 'app/core/models/device-summary-model';
|
import {DeviceSummaryModel} from 'app/core/models/device-summary-model';
|
||||||
import * as moment from 'moment';
|
import * as moment from 'moment';
|
||||||
import {HttpClientTestingModule} from '@angular/common/http/testing';
|
import {HttpClientTestingModule} from '@angular/common/http/testing';
|
||||||
|
import {HttpClient} from '@angular/common/http';
|
||||||
|
import {ScrutinyConfigService} from 'app/core/config/scrutiny-config.service';
|
||||||
|
import {of} from 'rxjs';
|
||||||
|
import {MetricsStatusThreshold} from 'app/core/config/app.config';
|
||||||
|
|
||||||
describe('DashboardDeviceComponent', () => {
|
describe('DashboardDeviceComponent', () => {
|
||||||
let component: DashboardDeviceComponent;
|
let component: DashboardDeviceComponent;
|
||||||
@@ -17,9 +21,14 @@ describe('DashboardDeviceComponent', () => {
|
|||||||
|
|
||||||
const matDialogSpy = jasmine.createSpyObj('MatDialog', ['open']);
|
const matDialogSpy = jasmine.createSpyObj('MatDialog', ['open']);
|
||||||
// const configServiceSpy = jasmine.createSpyObj('ScrutinyConfigService', ['config$']);
|
// const configServiceSpy = jasmine.createSpyObj('ScrutinyConfigService', ['config$']);
|
||||||
|
let configService: ScrutinyConfigService;
|
||||||
|
let httpClientSpy: jasmine.SpyObj<HttpClient>;
|
||||||
|
|
||||||
beforeEach(async(() => {
|
beforeEach(async(() => {
|
||||||
|
|
||||||
|
httpClientSpy = jasmine.createSpyObj('HttpClient', ['get']);
|
||||||
|
configService = new ScrutinyConfigService(httpClientSpy, {});
|
||||||
|
|
||||||
TestBed.configureTestingModule({
|
TestBed.configureTestingModule({
|
||||||
imports: [
|
imports: [
|
||||||
MatButtonModule,
|
MatButtonModule,
|
||||||
@@ -30,7 +39,8 @@ describe('DashboardDeviceComponent', () => {
|
|||||||
],
|
],
|
||||||
providers: [
|
providers: [
|
||||||
{provide: MatDialog, useValue: matDialogSpy},
|
{provide: MatDialog, useValue: matDialogSpy},
|
||||||
{provide: TREO_APP_CONFIG, useValue: {dashboard_display: 'name'}}
|
{provide: TREO_APP_CONFIG, useValue: {dashboard_display: 'name', metrics: {status_threshold: 3}}},
|
||||||
|
{provide: ScrutinyConfigService, useValue: configService}
|
||||||
],
|
],
|
||||||
declarations: [DashboardDeviceComponent]
|
declarations: [DashboardDeviceComponent]
|
||||||
})
|
})
|
||||||
@@ -50,25 +60,53 @@ describe('DashboardDeviceComponent', () => {
|
|||||||
describe('#classDeviceLastUpdatedOn()', () => {
|
describe('#classDeviceLastUpdatedOn()', () => {
|
||||||
|
|
||||||
it('if non-zero device status, should be red', () => {
|
it('if non-zero device status, should be red', () => {
|
||||||
|
httpClientSpy.get.and.returnValue(of({
|
||||||
|
settings: {
|
||||||
|
metrics: {
|
||||||
|
status_threshold: MetricsStatusThreshold.Both,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}));
|
||||||
|
component.ngOnInit()
|
||||||
// component.deviceSummary = summary.data.summary['0x5000c500673e6b5f'] as DeviceSummaryModel
|
// component.deviceSummary = summary.data.summary['0x5000c500673e6b5f'] as DeviceSummaryModel
|
||||||
expect(component.classDeviceLastUpdatedOn({
|
expect(component.classDeviceLastUpdatedOn({
|
||||||
device: {
|
device: {
|
||||||
device_status: 2
|
device_status: 2,
|
||||||
}
|
},
|
||||||
|
smart: {
|
||||||
|
collector_date: moment().subtract(13, 'days').toISOString()
|
||||||
|
},
|
||||||
} as DeviceSummaryModel)).toBe('text-red')
|
} as DeviceSummaryModel)).toBe('text-red')
|
||||||
});
|
});
|
||||||
|
|
||||||
it('if non-zero device status, should be red', () => {
|
it('if non-zero device status, should be red', () => {
|
||||||
// component.deviceSummary = summary.data.summary['0x5000c500673e6b5f'] as DeviceSummaryModel
|
httpClientSpy.get.and.returnValue(of({
|
||||||
|
settings: {
|
||||||
|
metrics: {
|
||||||
|
status_threshold: MetricsStatusThreshold.Both,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}));
|
||||||
|
component.ngOnInit()
|
||||||
expect(component.classDeviceLastUpdatedOn({
|
expect(component.classDeviceLastUpdatedOn({
|
||||||
device: {
|
device: {
|
||||||
device_status: 2
|
device_status: 2
|
||||||
}
|
},
|
||||||
|
smart: {
|
||||||
|
collector_date: moment().subtract(13, 'days').toISOString()
|
||||||
|
},
|
||||||
} as DeviceSummaryModel)).toBe('text-red')
|
} as DeviceSummaryModel)).toBe('text-red')
|
||||||
});
|
});
|
||||||
|
|
||||||
it('if healthy device status and updated in the last two weeks, should be green', () => {
|
it('if healthy device status and updated in the last two weeks, should be green', () => {
|
||||||
// component.deviceSummary = summary.data.summary['0x5000c500673e6b5f'] as DeviceSummaryModel
|
httpClientSpy.get.and.returnValue(of({
|
||||||
|
settings: {
|
||||||
|
metrics: {
|
||||||
|
status_threshold: MetricsStatusThreshold.Both,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}));
|
||||||
|
component.ngOnInit()
|
||||||
expect(component.classDeviceLastUpdatedOn({
|
expect(component.classDeviceLastUpdatedOn({
|
||||||
device: {
|
device: {
|
||||||
device_status: 0
|
device_status: 0
|
||||||
@@ -80,7 +118,14 @@ describe('DashboardDeviceComponent', () => {
|
|||||||
});
|
});
|
||||||
|
|
||||||
it('if healthy device status and updated more than two weeks ago, but less than 1 month, should be yellow', () => {
|
it('if healthy device status and updated more than two weeks ago, but less than 1 month, should be yellow', () => {
|
||||||
// component.deviceSummary = summary.data.summary['0x5000c500673e6b5f'] as DeviceSummaryModel
|
httpClientSpy.get.and.returnValue(of({
|
||||||
|
settings: {
|
||||||
|
metrics: {
|
||||||
|
status_threshold: MetricsStatusThreshold.Both,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}));
|
||||||
|
component.ngOnInit()
|
||||||
expect(component.classDeviceLastUpdatedOn({
|
expect(component.classDeviceLastUpdatedOn({
|
||||||
device: {
|
device: {
|
||||||
device_status: 0
|
device_status: 0
|
||||||
@@ -92,7 +137,14 @@ describe('DashboardDeviceComponent', () => {
|
|||||||
});
|
});
|
||||||
|
|
||||||
it('if healthy device status and updated more 1 month ago, should be red', () => {
|
it('if healthy device status and updated more 1 month ago, should be red', () => {
|
||||||
// component.deviceSummary = summary.data.summary['0x5000c500673e6b5f'] as DeviceSummaryModel
|
httpClientSpy.get.and.returnValue(of({
|
||||||
|
settings: {
|
||||||
|
metrics: {
|
||||||
|
status_threshold: MetricsStatusThreshold.Both,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}));
|
||||||
|
component.ngOnInit()
|
||||||
expect(component.classDeviceLastUpdatedOn({
|
expect(component.classDeviceLastUpdatedOn({
|
||||||
device: {
|
device: {
|
||||||
device_status: 0
|
device_status: 0
|
||||||
@@ -103,5 +155,4 @@ describe('DashboardDeviceComponent', () => {
|
|||||||
} as DeviceSummaryModel)).toBe('text-red')
|
} as DeviceSummaryModel)).toBe('text-red')
|
||||||
});
|
});
|
||||||
})
|
})
|
||||||
|
|
||||||
});
|
});
|
||||||
|
|||||||
+6
-19
@@ -9,6 +9,7 @@ import {MatDialog} from '@angular/material/dialog';
|
|||||||
import {DashboardDeviceDeleteDialogComponent} from 'app/layout/common/dashboard-device-delete-dialog/dashboard-device-delete-dialog.component';
|
import {DashboardDeviceDeleteDialogComponent} from 'app/layout/common/dashboard-device-delete-dialog/dashboard-device-delete-dialog.component';
|
||||||
import {DeviceTitlePipe} from 'app/shared/device-title.pipe';
|
import {DeviceTitlePipe} from 'app/shared/device-title.pipe';
|
||||||
import {DeviceSummaryModel} from 'app/core/models/device-summary-model';
|
import {DeviceSummaryModel} from 'app/core/models/device-summary-model';
|
||||||
|
import {DeviceStatusPipe} from 'app/shared/device-status.pipe';
|
||||||
|
|
||||||
@Component({
|
@Component({
|
||||||
selector: 'app-dashboard-device',
|
selector: 'app-dashboard-device',
|
||||||
@@ -35,6 +36,8 @@ export class DashboardDeviceComponent implements OnInit {
|
|||||||
|
|
||||||
readonly humanizeDuration = humanizeDuration;
|
readonly humanizeDuration = humanizeDuration;
|
||||||
|
|
||||||
|
deviceStatusForModelWithThreshold = DeviceStatusPipe.deviceStatusForModelWithThreshold
|
||||||
|
|
||||||
ngOnInit(): void {
|
ngOnInit(): void {
|
||||||
// Subscribe to config changes
|
// Subscribe to config changes
|
||||||
this._configService.config$
|
this._configService.config$
|
||||||
@@ -50,9 +53,10 @@ export class DashboardDeviceComponent implements OnInit {
|
|||||||
// -----------------------------------------------------------------------------------------------------
|
// -----------------------------------------------------------------------------------------------------
|
||||||
|
|
||||||
classDeviceLastUpdatedOn(deviceSummary: DeviceSummaryModel): string {
|
classDeviceLastUpdatedOn(deviceSummary: DeviceSummaryModel): string {
|
||||||
if (deviceSummary.device.device_status !== 0) {
|
const deviceStatus = DeviceStatusPipe.deviceStatusForModelWithThreshold(deviceSummary.device, !!deviceSummary.smart, this.config.metrics.status_threshold)
|
||||||
|
if (deviceStatus === 'failed') {
|
||||||
return 'text-red' // if the device has failed, always highlight in red
|
return 'text-red' // if the device has failed, always highlight in red
|
||||||
} else if (deviceSummary.device.device_status === 0 && deviceSummary.smart) {
|
} else if (deviceStatus === 'passed') {
|
||||||
if (moment().subtract(14, 'days').isBefore(deviceSummary.smart.collector_date)) {
|
if (moment().subtract(14, 'days').isBefore(deviceSummary.smart.collector_date)) {
|
||||||
// this device was updated in the last 2 weeks.
|
// this device was updated in the last 2 weeks.
|
||||||
return 'text-green'
|
return 'text-green'
|
||||||
@@ -68,23 +72,6 @@ export class DashboardDeviceComponent implements OnInit {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
deviceStatusString(deviceSummary: DeviceSummaryModel): string {
|
|
||||||
// no smart data, so treat the device status as unknown
|
|
||||||
if (!deviceSummary.smart) {
|
|
||||||
return 'unknown'
|
|
||||||
}
|
|
||||||
|
|
||||||
// determine the device status, by comparing it against the allowed threshold
|
|
||||||
// tslint:disable-next-line:no-bitwise
|
|
||||||
const deviceStatus = deviceSummary.device.device_status & this.config.metrics.status_threshold
|
|
||||||
if (deviceStatus === 0) {
|
|
||||||
return 'passed'
|
|
||||||
} else {
|
|
||||||
return 'failed'
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
openDeleteDialog(): void {
|
openDeleteDialog(): void {
|
||||||
const dialogRef = this.dialog.open(DashboardDeviceDeleteDialogComponent, {
|
const dialogRef = this.dialog.open(DashboardDeviceDeleteDialogComponent, {
|
||||||
// width: '250px',
|
// width: '250px',
|
||||||
|
|||||||
@@ -56,12 +56,13 @@
|
|||||||
<div *ngIf="device" class="my-2 col-span-2 lt-md:col-span-1">
|
<div *ngIf="device" class="my-2 col-span-2 lt-md:col-span-1">
|
||||||
<div>
|
<div>
|
||||||
<span class="inline-flex items-center font-bold text-xs px-2 py-2px rounded-full tracking-wide uppercase"
|
<span class="inline-flex items-center font-bold text-xs px-2 py-2px rounded-full tracking-wide uppercase"
|
||||||
[ngClass]="{'red-200': device?.device_status != 0,
|
[ngClass]="{'red-200': deviceStatusForModelWithThreshold(device, !!smart_results, config.metrics.status_threshold) == 'failed',
|
||||||
'green-200': device?.device_status == 0}">
|
'green-200': device?.device_status == 0}">
|
||||||
<span class="w-2 h-2 rounded-full mr-2"
|
<span class="w-2 h-2 rounded-full mr-2"
|
||||||
[ngClass]="{'bg-red': device?.device_status != 0,
|
[ngClass]="{'bg-red': device?.device_status != 0,
|
||||||
'bg-green': device?.device_status == 0}"></span>
|
'bg-green': device?.device_status == 0}"></span>
|
||||||
<span class="pr-2px leading-relaxed whitespace-no-wrap">{{device?.device_status | deviceStatus}}</span>
|
<span
|
||||||
|
class="pr-2px leading-relaxed whitespace-no-wrap">{{device | deviceStatus:!!smart_results:config.metrics.status_threshold:true}}</span>
|
||||||
</span>
|
</span>
|
||||||
</div>
|
</div>
|
||||||
<div class="text-secondary text-md">Status</div>
|
<div class="text-secondary text-md">Status</div>
|
||||||
|
|||||||
@@ -16,6 +16,7 @@ import {DeviceModel} from 'app/core/models/device-model';
|
|||||||
import {SmartModel} from 'app/core/models/measurements/smart-model';
|
import {SmartModel} from 'app/core/models/measurements/smart-model';
|
||||||
import {SmartAttributeModel} from 'app/core/models/measurements/smart-attribute-model';
|
import {SmartAttributeModel} from 'app/core/models/measurements/smart-attribute-model';
|
||||||
import {AttributeMetadataModel} from 'app/core/models/thresholds/attribute-metadata-model';
|
import {AttributeMetadataModel} from 'app/core/models/thresholds/attribute-metadata-model';
|
||||||
|
import {DeviceStatusPipe} from 'app/shared/device-status.pipe';
|
||||||
|
|
||||||
// from Constants.go - these must match
|
// from Constants.go - these must match
|
||||||
const AttributeStatusPassed = 0
|
const AttributeStatusPassed = 0
|
||||||
@@ -89,6 +90,7 @@ export class DetailComponent implements OnInit, AfterViewInit, OnDestroy {
|
|||||||
|
|
||||||
readonly humanizeDuration = humanizeDuration;
|
readonly humanizeDuration = humanizeDuration;
|
||||||
|
|
||||||
|
deviceStatusForModelWithThreshold = DeviceStatusPipe.deviceStatusForModelWithThreshold
|
||||||
// -----------------------------------------------------------------------------------------------------
|
// -----------------------------------------------------------------------------------------------------
|
||||||
// @ Lifecycle hooks
|
// @ Lifecycle hooks
|
||||||
// -----------------------------------------------------------------------------------------------------
|
// -----------------------------------------------------------------------------------------------------
|
||||||
|
|||||||
@@ -1,8 +1,146 @@
|
|||||||
import { DeviceStatusPipe } from './device-status.pipe';
|
import {DeviceStatusPipe} from './device-status.pipe';
|
||||||
|
import {MetricsStatusThreshold} from '../core/config/app.config';
|
||||||
|
import {DeviceModel} from '../core/models/device-model';
|
||||||
|
|
||||||
describe('DeviceStatusPipe', () => {
|
describe('DeviceStatusPipe', () => {
|
||||||
it('create an instance', () => {
|
it('create an instance', () => {
|
||||||
const pipe = new DeviceStatusPipe();
|
const pipe = new DeviceStatusPipe();
|
||||||
expect(pipe).toBeTruthy();
|
expect(pipe).toBeTruthy();
|
||||||
});
|
});
|
||||||
|
|
||||||
|
describe('#deviceStatusForModelWithThreshold', () => {
|
||||||
|
it('if healthy device, should be passing', () => {
|
||||||
|
expect(DeviceStatusPipe.deviceStatusForModelWithThreshold(
|
||||||
|
{device_status: 0} as DeviceModel,
|
||||||
|
true,
|
||||||
|
MetricsStatusThreshold.Both
|
||||||
|
)).toBe('passed')
|
||||||
|
});
|
||||||
|
|
||||||
|
it('if device with no smart data, should be unknown', () => {
|
||||||
|
expect(DeviceStatusPipe.deviceStatusForModelWithThreshold(
|
||||||
|
{device_status: 0} as DeviceModel,
|
||||||
|
false,
|
||||||
|
MetricsStatusThreshold.Both
|
||||||
|
)).toBe('unknown')
|
||||||
|
});
|
||||||
|
|
||||||
|
const testCases = [
|
||||||
|
{
|
||||||
|
'deviceStatus': 10000, // invalid status
|
||||||
|
'hasSmartResults': false,
|
||||||
|
'threshold': MetricsStatusThreshold.Smart,
|
||||||
|
'includeReason': false,
|
||||||
|
'result': 'unknown'
|
||||||
|
},
|
||||||
|
|
||||||
|
{
|
||||||
|
'deviceStatus': 1,
|
||||||
|
'hasSmartResults': true,
|
||||||
|
'threshold': MetricsStatusThreshold.Smart,
|
||||||
|
'includeReason': false,
|
||||||
|
'result': 'failed'
|
||||||
|
},
|
||||||
|
{
|
||||||
|
'deviceStatus': 1,
|
||||||
|
'hasSmartResults': true,
|
||||||
|
'threshold': MetricsStatusThreshold.Scrutiny,
|
||||||
|
'includeReason': false,
|
||||||
|
'result': 'passed'
|
||||||
|
},
|
||||||
|
{
|
||||||
|
'deviceStatus': 1,
|
||||||
|
'hasSmartResults': true,
|
||||||
|
'threshold': MetricsStatusThreshold.Both,
|
||||||
|
'includeReason': false,
|
||||||
|
'result': 'failed'
|
||||||
|
},
|
||||||
|
|
||||||
|
{
|
||||||
|
'deviceStatus': 2,
|
||||||
|
'hasSmartResults': true,
|
||||||
|
'threshold': MetricsStatusThreshold.Smart,
|
||||||
|
'includeReason': false,
|
||||||
|
'result': 'passed'
|
||||||
|
},
|
||||||
|
{
|
||||||
|
'deviceStatus': 2,
|
||||||
|
'hasSmartResults': true,
|
||||||
|
'threshold': MetricsStatusThreshold.Scrutiny,
|
||||||
|
'includeReason': false,
|
||||||
|
'result': 'failed'
|
||||||
|
},
|
||||||
|
{
|
||||||
|
'deviceStatus': 2,
|
||||||
|
'hasSmartResults': true,
|
||||||
|
'threshold': MetricsStatusThreshold.Both,
|
||||||
|
'includeReason': false,
|
||||||
|
'result': 'failed'
|
||||||
|
},
|
||||||
|
|
||||||
|
{
|
||||||
|
'deviceStatus': 3,
|
||||||
|
'hasSmartResults': true,
|
||||||
|
'threshold': MetricsStatusThreshold.Smart,
|
||||||
|
'includeReason': false,
|
||||||
|
'result': 'failed'
|
||||||
|
},
|
||||||
|
{
|
||||||
|
'deviceStatus': 3,
|
||||||
|
'hasSmartResults': true,
|
||||||
|
'threshold': MetricsStatusThreshold.Scrutiny,
|
||||||
|
'includeReason': false,
|
||||||
|
'result': 'failed'
|
||||||
|
},
|
||||||
|
{
|
||||||
|
'deviceStatus': 3,
|
||||||
|
'hasSmartResults': true,
|
||||||
|
'threshold': MetricsStatusThreshold.Both,
|
||||||
|
'includeReason': false,
|
||||||
|
'result': 'failed'
|
||||||
|
},
|
||||||
|
|
||||||
|
{
|
||||||
|
'deviceStatus': 3,
|
||||||
|
'hasSmartResults': false,
|
||||||
|
'threshold': MetricsStatusThreshold.Smart,
|
||||||
|
'includeReason': true,
|
||||||
|
'result': 'unknown'
|
||||||
|
},
|
||||||
|
{
|
||||||
|
'deviceStatus': 3,
|
||||||
|
'hasSmartResults': true,
|
||||||
|
'threshold': MetricsStatusThreshold.Smart,
|
||||||
|
'includeReason': true,
|
||||||
|
'result': 'failed: smart'
|
||||||
|
},
|
||||||
|
{
|
||||||
|
'deviceStatus': 3,
|
||||||
|
'hasSmartResults': true,
|
||||||
|
'threshold': MetricsStatusThreshold.Scrutiny,
|
||||||
|
'includeReason': true,
|
||||||
|
'result': 'failed: scrutiny'
|
||||||
|
},
|
||||||
|
{
|
||||||
|
'deviceStatus': 3,
|
||||||
|
'hasSmartResults': true,
|
||||||
|
'threshold': MetricsStatusThreshold.Both,
|
||||||
|
'includeReason': true,
|
||||||
|
'result': 'failed: both'
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
]
|
||||||
|
|
||||||
|
testCases.forEach((test, index) => {
|
||||||
|
it(`if device with status (${test.deviceStatus}), hasSmartResults(${test.hasSmartResults}) and threshold (${test.threshold}), should be ${test.result}`, () => {
|
||||||
|
expect(DeviceStatusPipe.deviceStatusForModelWithThreshold(
|
||||||
|
{device_status: test.deviceStatus} as DeviceModel,
|
||||||
|
test.hasSmartResults,
|
||||||
|
test.threshold,
|
||||||
|
test.includeReason
|
||||||
|
)).toBe(test.result)
|
||||||
|
});
|
||||||
|
});
|
||||||
|
});
|
||||||
});
|
});
|
||||||
|
|||||||
@@ -1,21 +1,71 @@
|
|||||||
import { Pipe, PipeTransform } from '@angular/core';
|
import {Pipe, PipeTransform} from '@angular/core';
|
||||||
|
import {MetricsStatusThreshold} from '../core/config/app.config';
|
||||||
|
import {DeviceModel} from '../core/models/device-model';
|
||||||
|
|
||||||
|
const DEVICE_STATUS_NAMES: { [key: number]: string } = {
|
||||||
|
0: 'passed',
|
||||||
|
1: 'failed',
|
||||||
|
2: 'failed',
|
||||||
|
3: 'failed'
|
||||||
|
};
|
||||||
|
|
||||||
|
const DEVICE_STATUS_NAMES_WITH_REASON: { [key: number]: string } = {
|
||||||
|
0: 'passed',
|
||||||
|
1: 'failed: smart',
|
||||||
|
2: 'failed: scrutiny',
|
||||||
|
3: 'failed: both'
|
||||||
|
};
|
||||||
|
|
||||||
|
|
||||||
@Pipe({
|
@Pipe({
|
||||||
name: 'deviceStatus'
|
name: 'deviceStatus'
|
||||||
})
|
})
|
||||||
export class DeviceStatusPipe implements PipeTransform {
|
export class DeviceStatusPipe implements PipeTransform {
|
||||||
|
|
||||||
transform(deviceStatusFlag: number): string {
|
|
||||||
if(deviceStatusFlag === 0){
|
static deviceStatusForModelWithThreshold(
|
||||||
return 'passed'
|
deviceModel: DeviceModel,
|
||||||
} else if(deviceStatusFlag === 3){
|
hasSmartResults: boolean = true,
|
||||||
return 'failed: both'
|
threshold: MetricsStatusThreshold = MetricsStatusThreshold.Both,
|
||||||
} else if(deviceStatusFlag === 2) {
|
includeReason: boolean = false
|
||||||
return 'failed: scrutiny'
|
): string {
|
||||||
} else if(deviceStatusFlag === 1) {
|
// no smart data, so treat the device status as unknown
|
||||||
return 'failed: smart'
|
if (!hasSmartResults) {
|
||||||
}
|
return 'unknown'
|
||||||
return 'unknown'
|
}
|
||||||
}
|
|
||||||
|
let statusNameLookup = DEVICE_STATUS_NAMES
|
||||||
|
if (includeReason) {
|
||||||
|
statusNameLookup = DEVICE_STATUS_NAMES_WITH_REASON
|
||||||
|
}
|
||||||
|
// determine the device status, by comparing it against the allowed threshold
|
||||||
|
// tslint:disable-next-line:no-bitwise
|
||||||
|
const deviceStatus = deviceModel.device_status & threshold
|
||||||
|
return statusNameLookup[deviceStatus]
|
||||||
|
}
|
||||||
|
|
||||||
|
// static deviceStatusForModelWithThreshold(deviceModel: DeviceModel | any, threshold: MetricsStatusThreshold): string {
|
||||||
|
// // tslint:disable-next-line:no-bitwise
|
||||||
|
// const deviceStatus = deviceModel?.device_status & threshold
|
||||||
|
// if(deviceStatus === 0){
|
||||||
|
// return 'passed'
|
||||||
|
// } else if(deviceStatus === 3){
|
||||||
|
// return 'failed: both'
|
||||||
|
// } else if(deviceStatus === 2) {
|
||||||
|
// return 'failed: scrutiny'
|
||||||
|
// } else if(deviceStatus === 1) {
|
||||||
|
// return 'failed: smart'
|
||||||
|
// }
|
||||||
|
// return 'unknown'
|
||||||
|
// }
|
||||||
|
|
||||||
|
transform(
|
||||||
|
deviceModel: DeviceModel,
|
||||||
|
hasSmartResults: boolean = true,
|
||||||
|
threshold: MetricsStatusThreshold = MetricsStatusThreshold.Both,
|
||||||
|
includeReason: boolean = false
|
||||||
|
): string {
|
||||||
|
return DeviceStatusPipe.deviceStatusForModelWithThreshold(deviceModel, hasSmartResults, threshold, includeReason)
|
||||||
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|||||||
Reference in New Issue
Block a user