From ad50839faedef565928e69ce274f3db89f9b3ac9 Mon Sep 17 00:00:00 2001 From: Chris Kaczor Date: Sun, 30 May 2021 19:10:22 -0400 Subject: [PATCH] Display app improvements - Add summary page - Use common time range component --- Display/src/app/app-routing.module.ts | 5 + Display/src/app/app.module.ts | 6 +- .../src/app/components/nav/nav.component.html | 9 ++ .../power/charts/power-charts.component.html | 34 +---- .../power/charts/power-charts.component.scss | 34 ++--- .../power/charts/power-charts.component.ts | 92 +++++------- .../time-range/time-range.component.html | 27 ++++ .../time-range/time-range.component.scss | 15 ++ .../time-range/time-range.component.spec.ts | 25 ++++ .../time-range/time-range.component.ts | 75 ++++++++++ .../charts/weather-charts.component.html | 39 ++--- .../charts/weather-charts.component.scss | 44 +++--- .../charts/weather-charts.component.ts | 108 ++++++-------- .../current/weather-current.component.html | 2 +- .../summary/weather-summary.component.html | 134 ++++++++++++++++++ .../summary/weather-summary.component.scss | 38 +++++ .../summary/weather-summary.component.spec.ts | 25 ++++ .../summary/weather-summary.component.ts | 79 +++++++++++ Display/src/app/models/time-span.ts | 5 + .../app/models/weather/weather-aggregates.ts | 15 ++ .../app/services/weather/weather.service.ts | 12 +- 21 files changed, 596 insertions(+), 227 deletions(-) create mode 100644 Display/src/app/components/time-range/time-range.component.html create mode 100644 Display/src/app/components/time-range/time-range.component.scss create mode 100644 Display/src/app/components/time-range/time-range.component.spec.ts create mode 100644 Display/src/app/components/time-range/time-range.component.ts create mode 100644 Display/src/app/components/weather/summary/weather-summary.component.html create mode 100644 Display/src/app/components/weather/summary/weather-summary.component.scss create mode 100644 Display/src/app/components/weather/summary/weather-summary.component.spec.ts create mode 100644 Display/src/app/components/weather/summary/weather-summary.component.ts create mode 100644 Display/src/app/models/time-span.ts create mode 100644 Display/src/app/models/weather/weather-aggregates.ts diff --git a/Display/src/app/app-routing.module.ts b/Display/src/app/app-routing.module.ts index e296ae5..2faff65 100644 --- a/Display/src/app/app-routing.module.ts +++ b/Display/src/app/app-routing.module.ts @@ -3,6 +3,7 @@ import { Routes, RouterModule } from '@angular/router'; import { DashboardComponent } from './components/dashboard/dashboard.component'; import { WeatherChartsComponent } from './components/weather/charts/weather-charts.component'; import { PowerChartsComponent } from './components/power/charts/power-charts.component'; +import { WeatherSummaryComponent } from './components/weather/summary/weather-summary.component'; const routes: Routes = [ { @@ -16,6 +17,10 @@ const routes: Routes = [ { path: 'power-charts', component: PowerChartsComponent + }, + { + path: 'weather-summary', + component: WeatherSummaryComponent } ]; diff --git a/Display/src/app/app.module.ts b/Display/src/app/app.module.ts index 993a41f..eac1829 100644 --- a/Display/src/app/app.module.ts +++ b/Display/src/app/app.module.ts @@ -32,6 +32,8 @@ import { WeatherCurrentComponent } from './components/weather/current/weather-cu import { AlmanacComponent } from './components/weather/almanac/almanac.component'; import { PowerComponent } from './components/power/current/power.component'; import { PowerChartsComponent } from './components/power/charts/power-charts.component'; +import { WeatherSummaryComponent } from './components/weather/summary/weather-summary.component'; +import { TimeRangeComponent } from './components/time-range/time-range.component'; const config: SocketIoConfig = { url: '/', options: {} }; @@ -45,7 +47,9 @@ const config: SocketIoConfig = { url: '/', options: {} }; WeatherCurrentComponent, AlmanacComponent, PowerComponent, - PowerChartsComponent + PowerChartsComponent, + WeatherSummaryComponent, + TimeRangeComponent ], imports: [ BrowserModule, diff --git a/Display/src/app/components/nav/nav.component.html b/Display/src/app/components/nav/nav.component.html index 71c66e1..241011a 100644 --- a/Display/src/app/components/nav/nav.component.html +++ b/Display/src/app/components/nav/nav.component.html @@ -16,6 +16,15 @@ + + + list + + + Summary + + + multiline_chart diff --git a/Display/src/app/components/power/charts/power-charts.component.html b/Display/src/app/components/power/charts/power-charts.component.html index 035ae2f..440ecff 100644 --- a/Display/src/app/components/power/charts/power-charts.component.html +++ b/Display/src/app/components/power/charts/power-charts.component.html @@ -1,33 +1,13 @@ -
-
- - - - - {{ item.value }} - - - - - - - - keyboard_arrow_down - - - - - - + +
+
diff --git a/Display/src/app/components/power/charts/power-charts.component.scss b/Display/src/app/components/power/charts/power-charts.component.scss index 05163c3..a0adba3 100644 --- a/Display/src/app/components/power/charts/power-charts.component.scss +++ b/Display/src/app/components/power/charts/power-charts.component.scss @@ -1,27 +1,19 @@ -.chart-content { +#refresh { + height: 36px; + top: 13px; +} + +.content { display: flex; flex-flow: column; height: 100%; - } +} - #chart { - flex: 1 1 auto; - } - - .chart-header { - background-color: rgb(250, 250, 250); +.header { + display: flex; padding: 0 20px; - flex: 0 0 auto; - } +} - .chart-button-spacer { - margin-right: 20px; - } - - .selected:after { - content: ""; - display: block; - margin: 0 auto; - width: 100%; - border-bottom: 1px solid #673ab7; - } +#chart { + flex: 1 1 auto; +} diff --git a/Display/src/app/components/power/charts/power-charts.component.ts b/Display/src/app/components/power/charts/power-charts.component.ts index 7d0f459..bcf477d 100644 --- a/Display/src/app/components/power/charts/power-charts.component.ts +++ b/Display/src/app/components/power/charts/power-charts.component.ts @@ -1,4 +1,5 @@ import { Component, OnInit } from '@angular/core'; +import { TimeSpan } from 'src/app/models/time-span'; import { Chart } from 'angular-highcharts'; import { SeriesLineOptions } from 'highcharts'; import { HttpClient } from '@angular/common/http'; @@ -13,12 +14,6 @@ import HC_exporting from 'highcharts/modules/exporting'; HC_exporting(Highcharts); -enum TimeSpan { - Last24Hours, - Day, - Custom -} - @Component({ selector: 'app-power-charts', templateUrl: './power-charts.component.html', @@ -28,60 +23,42 @@ export class PowerChartsComponent implements OnInit { public chart: Chart; public loading = true; - public timeSpanItems: { [value: number]: string } = {}; - public timeSpans: typeof TimeSpan = TimeSpan; - public maxDate: moment.Moment = moment().endOf('day'); - private selectedTimeSpanValue: TimeSpan = TimeSpan.Last24Hours; - private selectedDateValue: moment.Moment = moment().startOf('day'); + private timeSpanValue: TimeSpan = TimeSpan.Last24Hours; + private dateValue: moment.Moment = moment().startOf('day'); + + public get timeSpan(): TimeSpan { + return this.timeSpanValue; + } + public set timeSpan(value: TimeSpan) { + if (this.timeSpanValue === value) { + return; + } + this.timeSpanValue = value; + this.load(); + } + + public get date(): moment.Moment { + return this.dateValue; + } + public set date(value: moment.Moment) { + if (this.dateValue === value) { + return; + } + this.dateValue = value; + this.load(); + } private timeInterval = 15; constructor(private httpClient: HttpClient) { } ngOnInit() { - this.timeSpanItems[TimeSpan.Last24Hours] = 'Last 24 hours'; - this.timeSpanItems[TimeSpan.Day] = 'Day'; - - this.loadChart(); - } - - public get selectedTimeSpan() { - return this.selectedTimeSpanValue; - } - - public set selectedTimeSpan(value) { - this.selectedTimeSpanValue = value; - - this.loadChart(); - } - - public get selectedDate() { - return this.selectedDateValue; - } - - public set selectedDate(value) { - this.selectedDateValue = value; - - this.loadChart(); - } - - public handleDateArrowClick(value: number) { - this.selectedDate = moment(this.selectedDate).add(value, 'day'); - } - - public isSelectedDateToday(): boolean { - const isToday = moment(this.selectedDate).startOf('day').isSame(moment().startOf('day')); - - return isToday; - } - - public resetToToday() { - this.selectedDate = moment().startOf('day'); + this.load(); } public getSelectedDateDisplayString(): string { - return moment(this.selectedDate).format('LL'); + return moment(this.date).format('LL'); } private async loadPowerChart(start: moment.Moment, end: moment.Moment) { @@ -109,15 +86,14 @@ export class PowerChartsComponent implements OnInit { seriesData[2].data.push([date, dataElement.averageValue]); }); - const title = this.selectedTimeSpan === TimeSpan.Last24Hours ? this.timeSpanItems[TimeSpan.Last24Hours] : this.getSelectedDateDisplayString(); - this.chart = new Chart({ chart: { type: 'line', - zoomType: 'x' + zoomType: 'x', + spacingTop: 20 }, title: { - text: title + text: '' }, credits: { enabled: true @@ -185,7 +161,7 @@ export class PowerChartsComponent implements OnInit { }); } - public loadChart() { + public load() { let start: moment.Moment; let end: moment.Moment; @@ -195,7 +171,7 @@ export class PowerChartsComponent implements OnInit { this.chart.ref$.subscribe(o => o.showLoading()); } - switch (this.selectedTimeSpan) { + switch (this.timeSpan) { case TimeSpan.Last24Hours: { start = moment().subtract(24, 'hour'); end = moment(); @@ -204,8 +180,8 @@ export class PowerChartsComponent implements OnInit { } case TimeSpan.Day: { - start = moment(this.selectedDate).startOf('day'); - end = moment(this.selectedDate).endOf('day'); + start = moment(this.date).startOf('day'); + end = moment(this.date).endOf('day'); break; } diff --git a/Display/src/app/components/time-range/time-range.component.html b/Display/src/app/components/time-range/time-range.component.html new file mode 100644 index 0000000..5a3047f --- /dev/null +++ b/Display/src/app/components/time-range/time-range.component.html @@ -0,0 +1,27 @@ +
+
+ + + {{ item.value }} + + + + + + + + keyboard_arrow_down + + + + + + +
+
diff --git a/Display/src/app/components/time-range/time-range.component.scss b/Display/src/app/components/time-range/time-range.component.scss new file mode 100644 index 0000000..2583797 --- /dev/null +++ b/Display/src/app/components/time-range/time-range.component.scss @@ -0,0 +1,15 @@ +.content { + display: flex; + flex-flow: column; + height: 100%; +} + +.header { + background-color: rgb(250, 250, 250); + padding: 0 20px; + flex: 0 0 auto; +} + +.button-spacer { + margin-right: 20px; +} diff --git a/Display/src/app/components/time-range/time-range.component.spec.ts b/Display/src/app/components/time-range/time-range.component.spec.ts new file mode 100644 index 0000000..5ce96e3 --- /dev/null +++ b/Display/src/app/components/time-range/time-range.component.spec.ts @@ -0,0 +1,25 @@ +import { ComponentFixture, TestBed } from '@angular/core/testing'; + +import { TimeRangeComponent } from './time-range.component'; + +describe('TimeRangeComponent', () => { + let component: TimeRangeComponent; + let fixture: ComponentFixture; + + beforeEach(async () => { + await TestBed.configureTestingModule({ + declarations: [TimeRangeComponent] + }) + .compileComponents(); + }); + + beforeEach(() => { + fixture = TestBed.createComponent(TimeRangeComponent); + component = fixture.componentInstance; + fixture.detectChanges(); + }); + + it('should create', () => { + expect(component).toBeTruthy(); + }); +}); diff --git a/Display/src/app/components/time-range/time-range.component.ts b/Display/src/app/components/time-range/time-range.component.ts new file mode 100644 index 0000000..42ef3d5 --- /dev/null +++ b/Display/src/app/components/time-range/time-range.component.ts @@ -0,0 +1,75 @@ +import { Component, EventEmitter, Input, OnInit, Output } from '@angular/core'; +import { TimeSpan } from 'src/app/models/time-span'; + +import * as moment from 'moment'; + +@Component({ + selector: 'app-time-range', + templateUrl: './time-range.component.html', + styleUrls: ['./time-range.component.scss'] +}) +export class TimeRangeComponent implements OnInit { + + @Input() + public loading: boolean; + + private timeSpanValue: TimeSpan; + + @Input() + public set timeSpan(val: TimeSpan) { + this.timeSpanChange.emit(val); + this.timeSpanValue = val; + } + + public get timeSpan() { + return this.timeSpanValue; + } + + @Output() + public timeSpanChange: EventEmitter = new EventEmitter(); + + private dateValue: moment.Moment; + + @Input() + public set date(val: moment.Moment) { + this.dateChange.emit(val); + this.dateValue = val; + } + + public get date() { + return this.dateValue; + } + + @Output() + public dateChange: EventEmitter = new EventEmitter(); + + public timeSpanItems: { [value: number]: string } = { + [TimeSpan.Last24Hours]: 'Last 24 hours', + [TimeSpan.Day]: 'Day' + }; + + public timeSpans: typeof TimeSpan = TimeSpan; + public maxDate: moment.Moment = moment().endOf('day'); + + constructor() { } + + ngOnInit(): void { } + + public isSelectedDateToday(): boolean { + const isToday = moment(this.date).startOf('day').isSame(moment().startOf('day')); + + return isToday; + } + + public resetToToday() { + this.date = moment().startOf('day'); + } + + public getSelectedDateDisplayString(): string { + return moment(this.date).format('LL'); + } + + public handleDateArrowClick(value: number) { + this.date = moment(this.date).add(value, 'day'); + } +} diff --git a/Display/src/app/components/weather/charts/weather-charts.component.html b/Display/src/app/components/weather/charts/weather-charts.component.html index 2a53901..b19b59b 100644 --- a/Display/src/app/components/weather/charts/weather-charts.component.html +++ b/Display/src/app/components/weather/charts/weather-charts.component.html @@ -1,40 +1,19 @@ -
-
- - - - - - - - {{ item.value }} - - - - - - - - keyboard_arrow_down - - - - - - + +
+
diff --git a/Display/src/app/components/weather/charts/weather-charts.component.scss b/Display/src/app/components/weather/charts/weather-charts.component.scss index 03b97a5..5f07c6a 100644 --- a/Display/src/app/components/weather/charts/weather-charts.component.scss +++ b/Display/src/app/components/weather/charts/weather-charts.component.scss @@ -1,27 +1,31 @@ -.chart-content { - display: flex; - flex-flow: column; - height: 100%; +.header-button { + height: 36px; + top: 13px; +} + +.content { + display: flex; + flex-flow: column; + height: 100%; +} + +.header { + display: flex; + padding: 0 20px; } #chart { - flex: 1 1 auto; -} - -.chart-header { - background-color: rgb(250, 250, 250); - padding: 0 20px; - flex: 0 0 auto; -} - -.chart-button-spacer { - margin-right: 20px; + flex: 1 1 auto; } .selected:after { - content: ""; - display: block; - margin: 0 auto; - width: 100%; - border-bottom: 1px solid #673ab7; + content: ""; + display: block; + margin: 0 auto; + width: 100%; + border-bottom: 1px solid #673ab7; +} + +#time-range { + padding-left: 20px; } diff --git a/Display/src/app/components/weather/charts/weather-charts.component.ts b/Display/src/app/components/weather/charts/weather-charts.component.ts index 802ed08..9c18586 100644 --- a/Display/src/app/components/weather/charts/weather-charts.component.ts +++ b/Display/src/app/components/weather/charts/weather-charts.component.ts @@ -1,9 +1,10 @@ import { Component, OnInit } from '@angular/core'; +import { TimeSpan } from 'src/app/models/time-span'; import { Chart } from 'angular-highcharts'; import { SeriesLineOptions, SeriesWindbarbOptions, SeriesColumnOptions } from 'highcharts'; import { HttpClient } from '@angular/common/http'; -import { WeatherReadingGrouped } from '../../../models/weather/weather-reading-grouped'; -import { WindHistoryGrouped } from '../../../models/weather/wind-history-grouped'; +import { WeatherReadingGrouped } from 'src/app/models/weather/weather-reading-grouped'; +import { WindHistoryGrouped } from 'src/app/models/weather/wind-history-grouped'; import * as moment from 'moment'; @@ -13,12 +14,6 @@ import HC_windbarb from 'highcharts/modules/windbarb'; HC_exporting(Highcharts); HC_windbarb(Highcharts); -enum TimeSpan { - Last24Hours, - Day, - Custom -} - enum ChartType { Weather, Wind @@ -33,62 +28,41 @@ export class WeatherChartsComponent implements OnInit { public chart: Chart; public loading = true; - public timeSpanItems: { [value: number]: string } = {}; - public timeSpans: typeof TimeSpan = TimeSpan; - public maxDate: moment.Moment = moment().endOf('day'); + public selectedChartType: ChartType = ChartType.Weather; public ChartType = ChartType; - private selectedTimeSpanValue: TimeSpan = TimeSpan.Last24Hours; - private selectedDateValue: moment.Moment = moment().startOf('day'); + private timeSpanValue: TimeSpan = TimeSpan.Last24Hours; + private dateValue: moment.Moment = moment().startOf('day'); + + public get timeSpan(): TimeSpan { + return this.timeSpanValue; + } + public set timeSpan(value: TimeSpan) { + if (this.timeSpanValue === value) { + return; + } + this.timeSpanValue = value; + this.load(); + } + + public get date(): moment.Moment { + return this.dateValue; + } + public set date(value: moment.Moment) { + if (this.dateValue === value) { + return; + } + this.dateValue = value; + this.load(); + } private timeInterval = 15; constructor(private httpClient: HttpClient) { } ngOnInit() { - this.timeSpanItems[TimeSpan.Last24Hours] = 'Last 24 hours'; - this.timeSpanItems[TimeSpan.Day] = 'Day'; - - this.loadChart(); - } - - public get selectedTimeSpan() { - return this.selectedTimeSpanValue; - } - - public set selectedTimeSpan(value) { - this.selectedTimeSpanValue = value; - - this.loadChart(); - } - - public get selectedDate() { - return this.selectedDateValue; - } - - public set selectedDate(value) { - this.selectedDateValue = value; - - this.loadChart(); - } - - public handleDateArrowClick(value: number) { - this.selectedDate = moment(this.selectedDate).add(value, 'day'); - } - - public isSelectedDateToday(): boolean { - const isToday = moment(this.selectedDate).startOf('day').isSame(moment().startOf('day')); - - return isToday; - } - - public resetToToday() { - this.selectedDate = moment().startOf('day'); - } - - public getSelectedDateDisplayString(): string { - return moment(this.selectedDate).format('LL'); + this.load(); } public chartTypeChange(value: ChartType) { @@ -98,7 +72,7 @@ export class WeatherChartsComponent implements OnInit { this.selectedChartType = value; - this.loadChart(); + this.load(); } private loadWeatherChart(start: moment.Moment, end: moment.Moment) { @@ -125,14 +99,13 @@ export class WeatherChartsComponent implements OnInit { seriesData[4].data.push([date, dataElement.rainTotal]); }); - const title = this.selectedTimeSpan === TimeSpan.Last24Hours ? this.timeSpanItems[TimeSpan.Last24Hours] : this.getSelectedDateDisplayString(); - this.chart = new Chart({ chart: { - zoomType: 'x' + zoomType: 'x', + spacingTop: 20 }, title: { - text: title + text: '' }, credits: { enabled: true @@ -253,14 +226,13 @@ export class WeatherChartsComponent implements OnInit { seriesData[3].data.push([date, dataElement.averageSpeed, dataElement.averageDirection]); }); - const title = this.selectedTimeSpan === TimeSpan.Last24Hours ? this.timeSpanItems[TimeSpan.Last24Hours] : this.getSelectedDateDisplayString(); - this.chart = new Chart({ chart: { - zoomType: 'x' + zoomType: 'x', + spacingTop: 20 }, title: { - text: title + text: '' }, credits: { enabled: true @@ -319,7 +291,7 @@ export class WeatherChartsComponent implements OnInit { }); } - public loadChart() { + public load() { let start: moment.Moment; let end: moment.Moment; @@ -329,7 +301,7 @@ export class WeatherChartsComponent implements OnInit { this.chart.ref$.subscribe(o => o.showLoading()); } - switch (this.selectedTimeSpan) { + switch (this.timeSpan) { case TimeSpan.Last24Hours: { start = moment().subtract(24, 'hour'); end = moment(); @@ -338,8 +310,8 @@ export class WeatherChartsComponent implements OnInit { } case TimeSpan.Day: { - start = moment(this.selectedDate).startOf('day'); - end = moment(this.selectedDate).endOf('day'); + start = moment(this.date).startOf('day'); + end = moment(this.date).endOf('day'); break; } diff --git a/Display/src/app/components/weather/current/weather-current.component.html b/Display/src/app/components/weather/current/weather-current.component.html index f9e6c54..c4f7ae9 100644 --- a/Display/src/app/components/weather/current/weather-current.component.html +++ b/Display/src/app/components/weather/current/weather-current.component.html @@ -60,7 +60,7 @@ Wind - {{ latestReading.WindSpeed.toFixed(2) }} {{ latestReading.WindDirection }} + {{ latestReading.WindSpeed.toFixed(2) }} mph {{ latestReading.WindDirection }} diff --git a/Display/src/app/components/weather/summary/weather-summary.component.html b/Display/src/app/components/weather/summary/weather-summary.component.html new file mode 100644 index 0000000..042c14c --- /dev/null +++ b/Display/src/app/components/weather/summary/weather-summary.component.html @@ -0,0 +1,134 @@ + + +
+
+ + + +
+ +
+
+ Loading... +
+ +
+ No Data +
+ +
+ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
+ + + Minimum + + Average + + Maximum +
+ Temperature + + {{ weatherAggregates.temperature.min.toFixed(2) }}°F + + {{ weatherAggregates.temperature.average.toFixed(2) }}°F + + {{ weatherAggregates.temperature.max.toFixed(2) }}°F +
+ Humidity + + {{ weatherAggregates.humidity.min.toFixed(2) }}% + + {{ weatherAggregates.humidity.average.toFixed(2) }}% + + {{ weatherAggregates.humidity.max.toFixed(2) }}% +
+ Pressure + + {{ (weatherAggregates.pressure.min / 33.864 / 100).toFixed(2) }}" + + {{ (weatherAggregates.pressure.average / 33.864 / 100).toFixed(2) }}" + + {{ (weatherAggregates.pressure.max / 33.864 / 100).toFixed(2) }}" +
+ Light + + {{ weatherAggregates.light.min.toFixed(2) }} lx + + {{ weatherAggregates.light.average.toFixed(2) }} lx + + {{ weatherAggregates.light.max.toFixed(2) }} lx +
+ Wind Speed + + {{ weatherAggregates.windSpeed.min.toFixed(2) }} mph + + {{ weatherAggregates.windSpeed.average.toFixed(2) }} mph + + {{ weatherAggregates.windSpeed.max.toFixed(2) }} mph +
+ Wind Direction + + + {{ weatherAggregates.windDirectionAverage }} + +
+ Rain + + + + {{ weatherAggregates.rainTotal.toFixed(2) }}" +
+
+
+
diff --git a/Display/src/app/components/weather/summary/weather-summary.component.scss b/Display/src/app/components/weather/summary/weather-summary.component.scss new file mode 100644 index 0000000..6a91156 --- /dev/null +++ b/Display/src/app/components/weather/summary/weather-summary.component.scss @@ -0,0 +1,38 @@ +#refresh { + height: 36px; + top: 13px; +} + +.content { + height: 100%; +} + +.header { + display: flex; + padding: 0 20px; +} + +.body { + padding: 0 20px; + font-size: 14px; +} + +.button-spacer { + margin-right: 20px; +} + +.weather-summary-header { + font-weight: 500; + text-align: right; + padding-right: 20px; +} + +td { + padding-bottom: 4px; +} + +th { + font-weight: 500; + padding-right: 30px; + padding-bottom: 4px; +} diff --git a/Display/src/app/components/weather/summary/weather-summary.component.spec.ts b/Display/src/app/components/weather/summary/weather-summary.component.spec.ts new file mode 100644 index 0000000..6061c2c --- /dev/null +++ b/Display/src/app/components/weather/summary/weather-summary.component.spec.ts @@ -0,0 +1,25 @@ +import { ComponentFixture, TestBed } from '@angular/core/testing'; + +import { WeatherSummaryComponent } from './weather-summary.component'; + +describe('SummaryComponent', () => { + let component: WeatherSummaryComponent; + let fixture: ComponentFixture; + + beforeEach(async () => { + await TestBed.configureTestingModule({ + declarations: [WeatherSummaryComponent] + }) + .compileComponents(); + }); + + beforeEach(() => { + fixture = TestBed.createComponent(WeatherSummaryComponent); + component = fixture.componentInstance; + fixture.detectChanges(); + }); + + it('should create', () => { + expect(component).toBeTruthy(); + }); +}); diff --git a/Display/src/app/components/weather/summary/weather-summary.component.ts b/Display/src/app/components/weather/summary/weather-summary.component.ts new file mode 100644 index 0000000..29a1e55 --- /dev/null +++ b/Display/src/app/components/weather/summary/weather-summary.component.ts @@ -0,0 +1,79 @@ +import { Component, OnInit } from '@angular/core'; +import { TimeSpan } from 'src/app/models/time-span'; +import { WeatherService } from 'src/app/services/weather/weather.service'; +import { WeatherAggregates } from 'src/app/models/weather/weather-aggregates'; + +import * as moment from 'moment'; + +@Component({ + selector: 'app-weather-summary', + templateUrl: './weather-summary.component.html', + styleUrls: ['./weather-summary.component.scss'] +}) +export class WeatherSummaryComponent implements OnInit { + + public loading = true; + public weatherAggregates: WeatherAggregates = null; + + private timeSpanValue: TimeSpan = TimeSpan.Last24Hours; + private dateValue: moment.Moment = moment().startOf('day'); + + public get timeSpan(): TimeSpan { + return this.timeSpanValue; + } + public set timeSpan(value: TimeSpan) { + if (this.timeSpanValue === value) { + return; + } + this.timeSpanValue = value; + this.load(); + } + + public get date(): moment.Moment { + return this.dateValue; + } + public set date(value: moment.Moment) { + if (this.dateValue === value) { + return; + } + this.dateValue = value; + this.load(); + } + + constructor(private weatherService: WeatherService) { } + + ngOnInit(): void { + this.load(); + } + + public async load() { + let start: moment.Moment; + let end: moment.Moment; + + this.loading = true; + + switch (this.timeSpan) { + case TimeSpan.Last24Hours: { + start = moment().subtract(24, 'hour'); + end = moment(); + + break; + } + + case TimeSpan.Day: { + start = moment(this.date).startOf('day'); + end = moment(this.date).endOf('day'); + + break; + } + + default: { + return; + } + } + + this.weatherAggregates = await this.weatherService.getReadingAggregate(start, end); + + this.loading = false; + } +} diff --git a/Display/src/app/models/time-span.ts b/Display/src/app/models/time-span.ts new file mode 100644 index 0000000..4667ba8 --- /dev/null +++ b/Display/src/app/models/time-span.ts @@ -0,0 +1,5 @@ +export enum TimeSpan { + Last24Hours, + Day, + Custom +} diff --git a/Display/src/app/models/weather/weather-aggregates.ts b/Display/src/app/models/weather/weather-aggregates.ts new file mode 100644 index 0000000..6135d83 --- /dev/null +++ b/Display/src/app/models/weather/weather-aggregates.ts @@ -0,0 +1,15 @@ +export interface WeatherAggregates { + humidity: WeatherAggregate; + temperature: WeatherAggregate; + pressure: WeatherAggregate; + light: WeatherAggregate; + windSpeed: WeatherAggregate; + windDirectionAverage: number; + rainTotal: number; +} + +export interface WeatherAggregate { + min: number; + max: number; + average: number; +} diff --git a/Display/src/app/services/weather/weather.service.ts b/Display/src/app/services/weather/weather.service.ts index f5e1fd6..750268a 100644 --- a/Display/src/app/services/weather/weather.service.ts +++ b/Display/src/app/services/weather/weather.service.ts @@ -4,9 +4,10 @@ import { HubConnectionBuilder, HubConnection } from '@aspnet/signalr'; import { WeatherUpdate } from 'src/app/models/weather/weather-update'; import { WeatherValue } from 'src/app/models/weather/weather-value'; import { HttpClient } from '@angular/common/http'; +import { WeatherValueType } from 'src/app/models/weather/weather-value-type'; +import { WeatherAggregates } from 'src/app/models/weather/weather-aggregates'; import * as moment from 'moment'; -import { WeatherValueType } from 'src/app/models/weather/weather-value-type'; @Injectable({ providedIn: 'root' @@ -39,4 +40,13 @@ export class WeatherService { return data; } + + async getReadingAggregate(start: moment.Moment, end: moment.Moment): Promise { + const startString = start.toISOString(); + const endString = end.toISOString(); + + const data = await this.httpClient.get(`/api/weather/readings/aggregate?start=${startString}&end=${endString}`).toPromise(); + + return data; + } }