diff --git a/webapp/frontend/src/app/layout/common/dashboard-device/dashboard-device.component.spec.ts b/webapp/frontend/src/app/layout/common/dashboard-device/dashboard-device.component.spec.ts
index 3c6cc67..6e578a1 100644
--- a/webapp/frontend/src/app/layout/common/dashboard-device/dashboard-device.component.spec.ts
+++ b/webapp/frontend/src/app/layout/common/dashboard-device/dashboard-device.component.spec.ts
@@ -155,115 +155,4 @@ describe('DashboardDeviceComponent', () => {
} as DeviceSummaryModel)).toBe('text-red')
});
})
-
-
- describe('#deviceStatusString()', () => {
-
- it('if healthy device, should be passing', () => {
- httpClientSpy.get.and.returnValue(of({
- settings: {
- metrics: {
- status_threshold: MetricsStatusThreshold.Both,
- }
- }
- }));
- component.ngOnInit()
- expect(component.deviceStatusString({
- device: {
- device_status: 0
- },
- smart: {
- collector_date: moment().subtract(13, 'days').toISOString()
- },
- } as DeviceSummaryModel)).toBe('passed')
- });
-
- it('if device with no smart data, should be unknown', () => {
- httpClientSpy.get.and.returnValue(of({
- settings: {
- metrics: {
- status_threshold: MetricsStatusThreshold.Both,
- }
- }
- }));
- component.ngOnInit()
- expect(component.deviceStatusString({
- device: {
- device_status: 0
- },
- } as DeviceSummaryModel)).toBe('unknown')
- });
-
- const testCases = [
- {
- 'deviceStatus': 1,
- 'threshold': MetricsStatusThreshold.Smart,
- 'result': 'failed'
- },
- {
- 'deviceStatus': 1,
- 'threshold': MetricsStatusThreshold.Scrutiny,
- 'result': 'passed'
- },
- {
- 'deviceStatus': 1,
- 'threshold': MetricsStatusThreshold.Both,
- 'result': 'failed'
- },
-
- {
- 'deviceStatus': 2,
- 'threshold': MetricsStatusThreshold.Smart,
- 'result': 'passed'
- },
- {
- 'deviceStatus': 2,
- 'threshold': MetricsStatusThreshold.Scrutiny,
- 'result': 'failed'
- },
- {
- 'deviceStatus': 2,
- 'threshold': MetricsStatusThreshold.Both,
- 'result': 'failed'
- },
-
- {
- 'deviceStatus': 3,
- 'threshold': MetricsStatusThreshold.Smart,
- 'result': 'failed'
- },
- {
- 'deviceStatus': 3,
- 'threshold': MetricsStatusThreshold.Scrutiny,
- 'result': 'failed'
- },
- {
- 'deviceStatus': 3,
- 'threshold': MetricsStatusThreshold.Both,
- 'result': 'failed'
- }
-
- ]
-
- testCases.forEach((test, index) => {
- it(`if device with status (${test.deviceStatus}) and threshold (${test.threshold}), should be ${test.result}`, () => {
- httpClientSpy.get.and.returnValue(of({
- settings: {
- metrics: {
- status_threshold: test.threshold,
- }
- }
- }));
- component.ngOnInit()
- expect(component.deviceStatusString({
- device: {
- device_status: test.deviceStatus
- },
- smart: {
- collector_date: moment().subtract(13, 'days').toISOString()
- },
- } as DeviceSummaryModel)).toBe(test.result)
- });
- });
- })
});
diff --git a/webapp/frontend/src/app/layout/common/dashboard-device/dashboard-device.component.ts b/webapp/frontend/src/app/layout/common/dashboard-device/dashboard-device.component.ts
index 254f8c1..e29957e 100644
--- a/webapp/frontend/src/app/layout/common/dashboard-device/dashboard-device.component.ts
+++ b/webapp/frontend/src/app/layout/common/dashboard-device/dashboard-device.component.ts
@@ -9,8 +9,7 @@ import {MatDialog} from '@angular/material/dialog';
import {DashboardDeviceDeleteDialogComponent} from 'app/layout/common/dashboard-device-delete-dialog/dashboard-device-delete-dialog.component';
import {DeviceTitlePipe} from 'app/shared/device-title.pipe';
import {DeviceSummaryModel} from 'app/core/models/device-summary-model';
-
-export type deviceStatusName = 'unknown' | 'passed' | 'failed'
+import {DeviceStatusPipe} from 'app/shared/device-status.pipe';
@Component({
selector: 'app-dashboard-device',
@@ -37,6 +36,8 @@ export class DashboardDeviceComponent implements OnInit {
readonly humanizeDuration = humanizeDuration;
+ deviceStatusForModelWithThreshold = DeviceStatusPipe.deviceStatusForModelWithThreshold
+
ngOnInit(): void {
// Subscribe to config changes
this._configService.config$
@@ -52,7 +53,7 @@ export class DashboardDeviceComponent implements OnInit {
// -----------------------------------------------------------------------------------------------------
classDeviceLastUpdatedOn(deviceSummary: DeviceSummaryModel): string {
- const deviceStatus = this.deviceStatusString(deviceSummary)
+ 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
} else if (deviceStatus === 'passed') {
@@ -71,24 +72,6 @@ export class DashboardDeviceComponent implements OnInit {
}
}
-
- deviceStatusString(deviceSummary: DeviceSummaryModel): deviceStatusName {
- // 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 {
const dialogRef = this.dialog.open(DashboardDeviceDeleteDialogComponent, {
// width: '250px',
diff --git a/webapp/frontend/src/app/modules/detail/detail.component.html b/webapp/frontend/src/app/modules/detail/detail.component.html
index e96a493..d2ff848 100644
--- a/webapp/frontend/src/app/modules/detail/detail.component.html
+++ b/webapp/frontend/src/app/modules/detail/detail.component.html
@@ -56,12 +56,13 @@
- {{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)
+ }
}