diff --git a/webapp/backend/pkg/database/migrations/m20220716214900/setting.go b/webapp/backend/pkg/database/migrations/m20220716214900/setting.go
index 9c1f746..70d8d5e 100644
--- a/webapp/backend/pkg/database/migrations/m20220716214900/setting.go
+++ b/webapp/backend/pkg/database/migrations/m20220716214900/setting.go
@@ -14,4 +14,5 @@ type Setting struct {
SettingValueNumeric int `json:"setting_value_numeric"`
SettingValueString string `json:"setting_value_string"`
+ SettingValueBool bool `json:"setting_value_bool"`
}
diff --git a/webapp/backend/pkg/database/scrutiny_repository_migrations.go b/webapp/backend/pkg/database/scrutiny_repository_migrations.go
index a6f1b68..99eb1f3 100644
--- a/webapp/backend/pkg/database/scrutiny_repository_migrations.go
+++ b/webapp/backend/pkg/database/scrutiny_repository_migrations.go
@@ -319,6 +319,12 @@ func (sr *scrutinyRepository) Migrate(ctx context.Context) error {
SettingDataType: "string",
SettingValueString: "celsius",
},
+ {
+ SettingKeyName: "file_size_si_units",
+ SettingKeyDescription: "File size in SI units (true | false)",
+ SettingDataType: "bool",
+ SettingValueBool: false,
+ },
{
SettingKeyName: "metrics.notify_level",
diff --git a/webapp/backend/pkg/database/scrutiny_repository_settings.go b/webapp/backend/pkg/database/scrutiny_repository_settings.go
index 918a9f4..d92ce9b 100644
--- a/webapp/backend/pkg/database/scrutiny_repository_settings.go
+++ b/webapp/backend/pkg/database/scrutiny_repository_settings.go
@@ -24,6 +24,8 @@ func (sr *scrutinyRepository) LoadSettings(ctx context.Context) (*models.Setting
sr.appConfig.SetDefault(configKey, settingsEntry.SettingValueNumeric)
} else if settingsEntry.SettingDataType == "string" {
sr.appConfig.SetDefault(configKey, settingsEntry.SettingValueString)
+ } else if settingsEntry.SettingDataType == "bool" {
+ sr.appConfig.SetDefault(configKey, settingsEntry.SettingValueBool)
}
}
@@ -67,11 +69,13 @@ func (sr *scrutinyRepository) SaveSettings(ctx context.Context, settings models.
settingsEntries[ndx].SettingValueNumeric = sr.appConfig.GetInt(configKey)
} else if settingsEntry.SettingDataType == "string" {
settingsEntries[ndx].SettingValueString = sr.appConfig.GetString(configKey)
+ } else if settingsEntry.SettingDataType == "bool" {
+ settingsEntries[ndx].SettingValueBool = sr.appConfig.GetBool(configKey)
}
// store in database.
//TODO: this should be `sr.gormClient.Updates(&settingsEntries).Error`
- err := sr.gormClient.Model(&models.SettingEntry{}).Where([]uint{settingsEntry.ID}).Select("setting_value_numeric", "setting_value_string").Updates(settingsEntries[ndx]).Error
+ err := sr.gormClient.Model(&models.SettingEntry{}).Where([]uint{settingsEntry.ID}).Select("setting_value_numeric", "setting_value_string", "setting_value_bool").Updates(settingsEntries[ndx]).Error
if err != nil {
return err
}
diff --git a/webapp/backend/pkg/models/setting_entry.go b/webapp/backend/pkg/models/setting_entry.go
index 48d2c4c..2ac78f6 100644
--- a/webapp/backend/pkg/models/setting_entry.go
+++ b/webapp/backend/pkg/models/setting_entry.go
@@ -15,6 +15,7 @@ type SettingEntry struct {
SettingValueNumeric int `json:"setting_value_numeric"`
SettingValueString string `json:"setting_value_string"`
+ SettingValueBool bool `json:"setting_value_bool"`
}
func (s SettingEntry) TableName() string {
diff --git a/webapp/backend/pkg/models/settings.go b/webapp/backend/pkg/models/settings.go
index 48ba2d5..f06db84 100644
--- a/webapp/backend/pkg/models/settings.go
+++ b/webapp/backend/pkg/models/settings.go
@@ -13,6 +13,7 @@ type Settings struct {
DashboardDisplay string `json:"dashboard_display" mapstructure:"dashboard_display"`
DashboardSort string `json:"dashboard_sort" mapstructure:"dashboard_sort"`
TemperatureUnit string `json:"temperature_unit" mapstructure:"temperature_unit"`
+ FileSizeSIUnits bool `json:"file_size_si_units" mapstructure:"file_size_si_units"`
Metrics struct {
NotifyLevel int `json:"notify_level" mapstructure:"notify_level"`
diff --git a/webapp/frontend/src/app/core/config/app.config.ts b/webapp/frontend/src/app/core/config/app.config.ts
index b4a6114..92f0451 100644
--- a/webapp/frontend/src/app/core/config/app.config.ts
+++ b/webapp/frontend/src/app/core/config/app.config.ts
@@ -43,6 +43,8 @@ export interface AppConfig {
temperature_unit?: TemperatureUnit;
+ file_size_si_units?: boolean;
+
// Settings from Scrutiny API
metrics?: {
@@ -69,6 +71,8 @@ export const appConfig: AppConfig = {
dashboard_sort: 'status',
temperature_unit: 'celsius',
+ file_size_si_units: false,
+
metrics: {
notify_level: MetricsNotifyLevel.Fail,
status_filter_attributes: MetricsStatusFilterAttributes.All,
diff --git a/webapp/frontend/src/app/layout/common/dashboard-device/dashboard-device.component.html b/webapp/frontend/src/app/layout/common/dashboard-device/dashboard-device.component.html
index 7dfe40c..43e8964 100644
--- a/webapp/frontend/src/app/layout/common/dashboard-device/dashboard-device.component.html
+++ b/webapp/frontend/src/app/layout/common/dashboard-device/dashboard-device.component.html
@@ -58,7 +58,8 @@
Powered On
diff --git a/webapp/frontend/src/app/layout/common/dashboard-settings/dashboard-settings.component.html b/webapp/frontend/src/app/layout/common/dashboard-settings/dashboard-settings.component.html
index cde830a..750d54d 100644
--- a/webapp/frontend/src/app/layout/common/dashboard-settings/dashboard-settings.component.html
+++ b/webapp/frontend/src/app/layout/common/dashboard-settings/dashboard-settings.component.html
@@ -37,12 +37,20 @@
- Temperature Display Unit
+ Temperature
Celsius
Fahrenheit
+
+
+ File Size
+
+ SI Units (GB)
+ Binary Units (GiB)
+
+
diff --git a/webapp/frontend/src/app/layout/common/dashboard-settings/dashboard-settings.component.ts b/webapp/frontend/src/app/layout/common/dashboard-settings/dashboard-settings.component.ts
index 21e8e8e..6bc5f2a 100644
--- a/webapp/frontend/src/app/layout/common/dashboard-settings/dashboard-settings.component.ts
+++ b/webapp/frontend/src/app/layout/common/dashboard-settings/dashboard-settings.component.ts
@@ -22,6 +22,7 @@ export class DashboardSettingsComponent implements OnInit {
dashboardDisplay: string;
dashboardSort: string;
temperatureUnit: string;
+ fileSizeSIUnits: boolean;
theme: string;
statusThreshold: number;
statusFilterAttributes: number;
@@ -46,6 +47,7 @@ export class DashboardSettingsComponent implements OnInit {
this.dashboardDisplay = config.dashboard_display;
this.dashboardSort = config.dashboard_sort;
this.temperatureUnit = config.temperature_unit;
+ this.fileSizeSIUnits = config.file_size_si_units;
this.theme = config.theme;
this.statusFilterAttributes = config.metrics.status_filter_attributes;
@@ -60,6 +62,7 @@ export class DashboardSettingsComponent implements OnInit {
dashboard_display: this.dashboardDisplay as DashboardDisplay,
dashboard_sort: this.dashboardSort as DashboardSort,
temperature_unit: this.temperatureUnit as TemperatureUnit,
+ file_size_si_units: this.fileSizeSIUnits,
theme: this.theme as Theme,
metrics: {
status_filter_attributes: this.statusFilterAttributes as MetricsStatusFilterAttributes,
diff --git a/webapp/frontend/src/app/modules/detail/detail.component.html b/webapp/frontend/src/app/modules/detail/detail.component.html
index d2ff848..5c94592 100644
--- a/webapp/frontend/src/app/modules/detail/detail.component.html
+++ b/webapp/frontend/src/app/modules/detail/detail.component.html
@@ -107,7 +107,7 @@
Firmware Version
-
{{device?.capacity | fileSize}}
+
{{device?.capacity | fileSize:config.file_size_si_units}}
Capacity
diff --git a/webapp/frontend/src/app/shared/file-size.pipe.spec.ts b/webapp/frontend/src/app/shared/file-size.pipe.spec.ts
index 14973cf..0f2127a 100644
--- a/webapp/frontend/src/app/shared/file-size.pipe.spec.ts
+++ b/webapp/frontend/src/app/shared/file-size.pipe.spec.ts
@@ -1,4 +1,4 @@
-import { FileSizePipe } from './file-size.pipe';
+import {FileSizePipe} from './file-size.pipe';
describe('FileSizePipe', () => {
it('create an instance', () => {
@@ -10,23 +10,61 @@ describe('FileSizePipe', () => {
const testCases = [
{
'bytes': 1500,
- 'precision': undefined,
- 'result': '1 KB'
- },{
- 'bytes': 2_100_000_000,
- 'precision': undefined,
- 'result': '2.0 GB',
- },{
+ 'si': false,
+ 'result': '1.5 KiB'
+ },
+ {
'bytes': 1500,
- 'precision': 2,
- 'result': '1.46 KB',
+ 'si': true,
+ 'result': '1.5 kB'
+ },
+ {
+ 'bytes': 5000,
+ 'si': false,
+ 'result': '4.9 KiB',
+ },
+ {
+ 'bytes': 5000,
+ 'si': true,
+ 'result': '5.0 kB',
+ },
+ {
+ 'bytes': 999_949,
+ 'si': false,
+ 'result': '976.5 KiB',
+ },
+ {
+ 'bytes': 999_949,
+ 'si': true,
+ 'result': '999.9 kB',
+ },
+ {
+ 'bytes': 999_950,
+ 'si': true,
+ 'result': '1.0 MB',
+ },
+ {
+ 'bytes': 1_551_859_712,
+ 'si': false,
+ 'result': '1.4 GiB',
+ },
+ {
+ 'bytes': 2_100_000_000,
+ 'si': false,
+ 'result': '2.0 GiB',
+ },
+ {
+ 'bytes': 2_100_000_000,
+ 'si': true,
+ 'result': '2.1 GB',
}
]
+
testCases.forEach((test, index) => {
it(`should correctly format bytes ${test.bytes}. (testcase: ${index + 1})`, () => {
// test
const pipe = new FileSizePipe();
- const formatted = pipe.transform(test.bytes, test.precision)
+ const formatted = pipe.transform(test.bytes, test.si)
expect(formatted).toEqual(test.result);
});
})
diff --git a/webapp/frontend/src/app/shared/file-size.pipe.ts b/webapp/frontend/src/app/shared/file-size.pipe.ts
index e6cbc7b..14fdf0c 100644
--- a/webapp/frontend/src/app/shared/file-size.pipe.ts
+++ b/webapp/frontend/src/app/shared/file-size.pipe.ts
@@ -1,75 +1,27 @@
-/**
- * @license
- * Copyright (c) 2019 Jonathan Catmull.
- *
- * Permission is hereby granted, free of charge, to any person obtaining a copy
- * of this software and associated documentation files (the "Software"), to deal
- * in the Software without restriction, including without limitation the rights
- * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
- * copies of the Software, and to permit persons to whom the Software is
- * furnished to do so, subject to the following conditions:
- *
- * The above copyright notice and this permission notice shall be included in all
- * copies or substantial portions of the Software.
- *
- * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
- * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
- * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
- * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
- * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
- * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
- * SOFTWARE.
- */
-import { Pipe, PipeTransform } from '@angular/core';
+import {Pipe, PipeTransform} from '@angular/core';
-type unit = 'bytes' | 'KB' | 'MB' | 'GB' | 'TB' | 'PB';
-type unitPrecisionMap = {
- [u in unit]: number;
-};
-
-const defaultPrecisionMap: unitPrecisionMap = {
- bytes: 0,
- KB: 0,
- MB: 1,
- GB: 1,
- TB: 2,
- PB: 2
-};
-
-/*
- * Convert bytes into largest possible unit.
- * Takes an precision argument that can be a number or a map for each unit.
- * Usage:
- * bytes | fileSize:precision
- * @example
- * // returns 1 KB
- * {{ 1500 | fileSize }}
- * @example
- * // returns 2.1 GB
- * {{ 2100000000 | fileSize }}
- * @example
- * // returns 1.46 KB
- * {{ 1500 | fileSize:2 }}
- */
-@Pipe({ name: 'fileSize' })
+@Pipe({name: 'fileSize'})
export class FileSizePipe implements PipeTransform {
- private readonly units: unit[] = ['bytes', 'KB', 'MB', 'GB', 'TB', 'PB'];
- transform(bytes: number = 0, precision: number | unitPrecisionMap = defaultPrecisionMap): string {
- if (isNaN(parseFloat(String(bytes))) || !isFinite(bytes)) return '?';
+ transform(bytes: number = 0, si = false, dp = 1): string {
+ const thresh = si ? 1000 : 1024;
- let unitIndex = 0;
-
- while (bytes >= 1024) {
- bytes /= 1024;
- unitIndex++;
+ if (Math.abs(bytes) < thresh) {
+ return bytes + ' B';
}
- const unit = this.units[unitIndex];
+ const units = si
+ ? ['kB', 'MB', 'GB', 'TB', 'PB', 'EB', 'ZB', 'YB']
+ : ['KiB', 'MiB', 'GiB', 'TiB', 'PiB', 'EiB', 'ZiB', 'YiB'];
+ let u = -1;
+ const r = 10 ** dp;
- if (typeof precision === 'number') {
- return `${bytes.toFixed(+precision)} ${unit}`;
- }
- return `${bytes.toFixed(precision[unit])} ${unit}`;
+ do {
+ bytes /= thresh;
+ ++u;
+ } while (Math.round(Math.abs(bytes) * r) / r >= thresh && u < units.length - 1);
+
+
+ return bytes.toFixed(dp) + ' ' + units[u];
}
}