- {{device?.device_status | deviceStatus}}
+ {{device | deviceStatus:!!smart_results:config.metrics.status_threshold:true}}
Status
diff --git a/webapp/frontend/src/app/modules/detail/detail.component.ts b/webapp/frontend/src/app/modules/detail/detail.component.ts
index 1353c51..ce21642 100644
--- a/webapp/frontend/src/app/modules/detail/detail.component.ts
+++ b/webapp/frontend/src/app/modules/detail/detail.component.ts
@@ -16,6 +16,7 @@ import {DeviceModel} from 'app/core/models/device-model';
import {SmartModel} from 'app/core/models/measurements/smart-model';
import {SmartAttributeModel} from 'app/core/models/measurements/smart-attribute-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
const AttributeStatusPassed = 0
@@ -89,6 +90,7 @@ export class DetailComponent implements OnInit, AfterViewInit, OnDestroy {
readonly humanizeDuration = humanizeDuration;
+ deviceStatusForModelWithThreshold = DeviceStatusPipe.deviceStatusForModelWithThreshold
// -----------------------------------------------------------------------------------------------------
// @ Lifecycle hooks
// -----------------------------------------------------------------------------------------------------
diff --git a/webapp/frontend/src/app/shared/device-status.pipe.spec.ts b/webapp/frontend/src/app/shared/device-status.pipe.spec.ts
index 23bc958..57d9e7c 100644
--- a/webapp/frontend/src/app/shared/device-status.pipe.spec.ts
+++ b/webapp/frontend/src/app/shared/device-status.pipe.spec.ts
@@ -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', () => {
- it('create an instance', () => {
- const pipe = new DeviceStatusPipe();
- expect(pipe).toBeTruthy();
- });
+ it('create an instance', () => {
+ const pipe = new DeviceStatusPipe();
+ 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)
+ });
+ });
+ });
});
diff --git a/webapp/frontend/src/app/shared/device-status.pipe.ts b/webapp/frontend/src/app/shared/device-status.pipe.ts
index 42261c6..68a692d 100644
--- a/webapp/frontend/src/app/shared/device-status.pipe.ts
+++ b/webapp/frontend/src/app/shared/device-status.pipe.ts
@@ -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({
- name: 'deviceStatus'
+ name: 'deviceStatus'
})
export class DeviceStatusPipe implements PipeTransform {
- transform(deviceStatusFlag: number): string {
- if(deviceStatusFlag === 0){
- return 'passed'
- } else if(deviceStatusFlag === 3){
- return 'failed: both'
- } else if(deviceStatusFlag === 2) {
- return 'failed: scrutiny'
- } else if(deviceStatusFlag === 1) {
- return 'failed: smart'
- }
- return 'unknown'
- }
+
+ static deviceStatusForModelWithThreshold(
+ deviceModel: DeviceModel,
+ hasSmartResults: boolean = true,
+ threshold: MetricsStatusThreshold = MetricsStatusThreshold.Both,
+ includeReason: boolean = false
+ ): string {
+ // no smart data, so treat the device status as unknown
+ if (!hasSmartResults) {
+ 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)
+ }
}