From ebbe0cd3633a33162b9c71cd48bc0c557acc71e6 Mon Sep 17 00:00:00 2001 From: Jan-Gerrit Goebel Date: Tue, 7 May 2024 09:09:54 +0200 Subject: [PATCH 1/6] Improve chart by caching progresses --- .../session-statistics.component.ts | 67 +++++++++++++------ 1 file changed, 46 insertions(+), 21 deletions(-) diff --git a/src/app/session-statistics/session-statistics.component.ts b/src/app/session-statistics/session-statistics.component.ts index 9a777281..5c17fd38 100644 --- a/src/app/session-statistics/session-statistics.component.ts +++ b/src/app/session-statistics/session-statistics.component.ts @@ -3,7 +3,6 @@ import { Input, OnChanges, OnInit, - SimpleChanges, ViewChild, } from '@angular/core'; import { ChartConfiguration, ChartData, ChartEvent, ChartType } from 'chart.js'; @@ -39,11 +38,17 @@ const ONE_DAY = 1000 * 60 * 60 * 24; styleUrls: ['./session-statistics.component.scss'], }) export class SessionStatisticsComponent implements OnInit, OnChanges { + + // If no scheduledEvent is given, we display statistics about all progresses for a given time range + // If a scheduledEvent is given, we display statistics about all progresses from this scheduledEvent @Input() public scheduledEvent: ScheduledEvent; public currentScheduledEvent: ScheduledEvent; + // ProgressesCache is used only when scheduledEvent is given. We only need to retreive data once and afterwards only apply filters for the time range, observation period, selected scenarios etc. + public progressesCache: Progress[]; + public startView: 'minute' | 'day' | 'month' | 'year' = 'day'; public minView: 'minute' | 'day' | 'month' | 'year' = 'day'; public options: Intl.DateTimeFormatOptions = { @@ -123,7 +128,11 @@ export class SessionStatisticsComponent implements OnInit, OnChanges { this.currentScheduledEvent ) { this.currentScheduledEvent = this.scheduledEvent; - this.setDatesToScheduledEvent(this.scheduledEvent); + this.progressesCache = null; // Reset cache so data from the changed SE can be retreived + this.scenariosWithSession = []; + this.totalSessionsPerScenario = new Map(); + this.chartDetails.controls.scenarios.setValue(["*"]); + this.setDatesToScheduledEvent(this.scheduledEvent, this.chartDetails.controls.observationPeriod.value); this.updateData(this.chartDetails.controls.observationPeriod.value); } } @@ -174,7 +183,7 @@ export class SessionStatisticsComponent implements OnInit, OnChanges { ); if (this.scheduledEvent) { - this.setDatesToScheduledEvent(this.scheduledEvent); + this.setDatesToScheduledEvent(this.scheduledEvent, 'daily'); } this.updateLabels('daily'); @@ -248,7 +257,7 @@ export class SessionStatisticsComponent implements OnInit, OnChanges { }; } - private setDatesToScheduledEvent(se: ScheduledEvent) { + private setDatesToScheduledEvent(se: ScheduledEvent, observationPeriod: "daily" | "weekly" | "monthly") { const currentDate = new Date(); // Set default start date to beginning of the scheduledEvent @@ -261,12 +270,13 @@ export class SessionStatisticsComponent implements OnInit, OnChanges { this.endDate = currentDate; } - if (this.chartDetails) { - this.chartDetails.controls.startDate.setValue( - this.startDate.toDateString() - ); - this.chartDetails.controls.endDate.setValue(this.endDate.toDateString()); - } + this.chartDetails.controls.endDate.setValue( + this.endDate.toLocaleDateString('en-US', this.options) + ); + this.chartDetails.controls.startDate.setValue( + this.startDate.toLocaleDateString('en-US', this.options) + ); + this.updateLabels(observationPeriod) } private updateData(observationPeriod: 'daily' | 'weekly' | 'monthly') { @@ -288,13 +298,16 @@ export class SessionStatisticsComponent implements OnInit, OnChanges { private updateDataByScheduledEvent( observationPeriod: 'daily' | 'weekly' | 'monthly' ) { - this.progressService + if(this.progressesCache){ + this.processData(this.progressesCache, observationPeriod); + }else{ + this.progressService .listByScheduledEvent(this.scheduledEvent.id, true) .subscribe((progresses: Progress[]) => { - // TODO: Filter by range - progresses.forEach((p) => {}); - this.processData(progresses, observationPeriod); + this.progressesCache = progresses + this.processData(this.progressesCache, observationPeriod); }); + } } private processData( @@ -362,8 +375,8 @@ export class SessionStatisticsComponent implements OnInit, OnChanges { } } - public setStartDate(d: DlDateTimePickerChange) { - this.startDate = d.value; + private updateStartDate(d: Date){ + this.startDate = d; this.chartDetails.controls.startDate.setValue( this.startDate.toLocaleDateString('en-US', this.options) ); @@ -371,16 +384,22 @@ export class SessionStatisticsComponent implements OnInit, OnChanges { this.chartDetails.controls.observationPeriod.value; this.updateLabels(observationPeriod); this.updateData(observationPeriod); - this.startDateSignpost.close(); } - public setEndDate(d: DlDateTimePickerChange) { + public setStartDate(d: DlDateTimePickerChange) { + this.updateStartDate(d.value); + if(this.startDateSignpost){ + this.startDateSignpost.close(); + } + } + + private updateEndDate(d: Date){ const observationPeriod: 'daily' | 'weekly' | 'monthly' = this.chartDetails.controls.observationPeriod.value; if (observationPeriod != 'monthly') { - this.endDate = d.value; + this.endDate = d; } else { - this.endDate = new Date(d.value.getFullYear(), d.value.getMonth() + 1, 0); + this.endDate = new Date(d.getFullYear(), d.getMonth() + 1, 0); } this.endDate.setHours(23, 59, 59, 999); this.updateLabels(observationPeriod); @@ -388,7 +407,13 @@ export class SessionStatisticsComponent implements OnInit, OnChanges { this.chartDetails.controls.endDate.setValue( this.endDate.toLocaleDateString('en-US', this.options) ); - this.endDateSignpost.close(); + } + + public setEndDate(d: DlDateTimePickerChange) { + this.updateEndDate(d.value); + if(this.endDateSignpost){ + this.endDateSignpost.close(); + } } // events From 7af51f118e4203cb234a1f03241b2224ea9c0fbb Mon Sep 17 00:00:00 2001 From: Jan-Gerrit Goebel Date: Tue, 7 May 2024 13:07:37 +0200 Subject: [PATCH 2/6] Add chart that displays the average duration inside a scenario --- src/app/app.module.ts | 2 + .../session-statistics.component.html | 10 + .../session-statistics.component.ts | 47 ++-- .../session-time-statistics.component.html | 54 ++++ .../session-time-statistics.component.scss | 0 .../session-time-statistics.component.ts | 265 ++++++++++++++++++ src/app/utils.ts | 73 +++-- 7 files changed, 404 insertions(+), 47 deletions(-) create mode 100644 src/app/session-statistics/session-time-statistics/session-time-statistics.component.html create mode 100644 src/app/session-statistics/session-time-statistics/session-time-statistics.component.scss create mode 100644 src/app/session-statistics/session-time-statistics/session-time-statistics.component.ts diff --git a/src/app/app.module.ts b/src/app/app.module.ts index 7e6ec6ff..f891077f 100644 --- a/src/app/app.module.ts +++ b/src/app/app.module.ts @@ -83,6 +83,7 @@ import { EnvironmentDetailComponent } from './configuration/environments/environ import { VmTemplateDetailComponent } from './configuration/vmtemplates/vmtemplate-detail/vmtemplate-detail.component'; import { NgChartsModule } from 'ng2-charts'; import { SessionStatisticsComponent } from './session-statistics/session-statistics.component'; +import { SessionTimeStatisticsComponent } from './session-statistics/session-time-statistics/session-time-statistics.component'; import { VMTemplateServiceFormComponent } from './configuration/vmtemplates/edit-vmtemplate/vmtemplate-service-form/vmtemplate-service-form.component'; import { FilterScenariosComponent } from './filter-scenarios/filter-scenarios.component'; import { MDEditorComponent } from './scenario/md-editor/md-editor.component'; @@ -211,6 +212,7 @@ export function jwtOptionsFactory(): JwtConfig { AppComponent, HomeComponent, SessionStatisticsComponent, + SessionTimeStatisticsComponent, HeaderComponent, EventComponent, LoginComponent, diff --git a/src/app/session-statistics/session-statistics.component.html b/src/app/session-statistics/session-statistics.component.html index b4e9e687..70bdc1a8 100644 --- a/src/app/session-statistics/session-statistics.component.html +++ b/src/app/session-statistics/session-statistics.component.html @@ -171,4 +171,14 @@

Started Sessions (List)

+

Scenario Statistics

+
+
+ + +
+
diff --git a/src/app/session-statistics/session-statistics.component.ts b/src/app/session-statistics/session-statistics.component.ts index 5c17fd38..6cd4f2e6 100644 --- a/src/app/session-statistics/session-statistics.component.ts +++ b/src/app/session-statistics/session-statistics.component.ts @@ -1,10 +1,4 @@ -import { - Component, - Input, - OnChanges, - OnInit, - ViewChild, -} from '@angular/core'; +import { Component, Input, OnChanges, OnInit, ViewChild } from '@angular/core'; import { ChartConfiguration, ChartData, ChartEvent, ChartType } from 'chart.js'; import { BaseChartDirective } from 'ng2-charts'; import DataLabelsPlugin, { Context } from 'chartjs-plugin-datalabels'; @@ -38,7 +32,6 @@ const ONE_DAY = 1000 * 60 * 60 * 24; styleUrls: ['./session-statistics.component.scss'], }) export class SessionStatisticsComponent implements OnInit, OnChanges { - // If no scheduledEvent is given, we display statistics about all progresses for a given time range // If a scheduledEvent is given, we display statistics about all progresses from this scheduledEvent @Input() @@ -46,7 +39,6 @@ export class SessionStatisticsComponent implements OnInit, OnChanges { public currentScheduledEvent: ScheduledEvent; - // ProgressesCache is used only when scheduledEvent is given. We only need to retreive data once and afterwards only apply filters for the time range, observation period, selected scenarios etc. public progressesCache: Progress[]; public startView: 'minute' | 'day' | 'month' | 'year' = 'day'; @@ -131,8 +123,11 @@ export class SessionStatisticsComponent implements OnInit, OnChanges { this.progressesCache = null; // Reset cache so data from the changed SE can be retreived this.scenariosWithSession = []; this.totalSessionsPerScenario = new Map(); - this.chartDetails.controls.scenarios.setValue(["*"]); - this.setDatesToScheduledEvent(this.scheduledEvent, this.chartDetails.controls.observationPeriod.value); + this.chartDetails.controls.scenarios.setValue(['*']); + this.setDatesToScheduledEvent( + this.scheduledEvent, + this.chartDetails.controls.observationPeriod.value + ); this.updateData(this.chartDetails.controls.observationPeriod.value); } } @@ -257,7 +252,10 @@ export class SessionStatisticsComponent implements OnInit, OnChanges { }; } - private setDatesToScheduledEvent(se: ScheduledEvent, observationPeriod: "daily" | "weekly" | "monthly") { + private setDatesToScheduledEvent( + se: ScheduledEvent, + observationPeriod: 'daily' | 'weekly' | 'monthly' + ) { const currentDate = new Date(); // Set default start date to beginning of the scheduledEvent @@ -276,7 +274,7 @@ export class SessionStatisticsComponent implements OnInit, OnChanges { this.chartDetails.controls.startDate.setValue( this.startDate.toLocaleDateString('en-US', this.options) ); - this.updateLabels(observationPeriod) + this.updateLabels(observationPeriod); } private updateData(observationPeriod: 'daily' | 'weekly' | 'monthly') { @@ -291,6 +289,7 @@ export class SessionStatisticsComponent implements OnInit, OnChanges { this.progressService .listByRange(this.startDate, this.endDate) .subscribe((progresses: Progress[]) => { + this.progressesCache = progresses; this.processData(progresses, observationPeriod); }); } @@ -298,15 +297,15 @@ export class SessionStatisticsComponent implements OnInit, OnChanges { private updateDataByScheduledEvent( observationPeriod: 'daily' | 'weekly' | 'monthly' ) { - if(this.progressesCache){ + if (this.progressesCache) { this.processData(this.progressesCache, observationPeriod); - }else{ + } else { this.progressService - .listByScheduledEvent(this.scheduledEvent.id, true) - .subscribe((progresses: Progress[]) => { - this.progressesCache = progresses - this.processData(this.progressesCache, observationPeriod); - }); + .listByScheduledEvent(this.scheduledEvent.id, true) + .subscribe((progresses: Progress[]) => { + this.progressesCache = progresses; + this.processData(this.progressesCache, observationPeriod); + }); } } @@ -375,7 +374,7 @@ export class SessionStatisticsComponent implements OnInit, OnChanges { } } - private updateStartDate(d: Date){ + private updateStartDate(d: Date) { this.startDate = d; this.chartDetails.controls.startDate.setValue( this.startDate.toLocaleDateString('en-US', this.options) @@ -388,12 +387,12 @@ export class SessionStatisticsComponent implements OnInit, OnChanges { public setStartDate(d: DlDateTimePickerChange) { this.updateStartDate(d.value); - if(this.startDateSignpost){ + if (this.startDateSignpost) { this.startDateSignpost.close(); } } - private updateEndDate(d: Date){ + private updateEndDate(d: Date) { const observationPeriod: 'daily' | 'weekly' | 'monthly' = this.chartDetails.controls.observationPeriod.value; if (observationPeriod != 'monthly') { @@ -411,7 +410,7 @@ export class SessionStatisticsComponent implements OnInit, OnChanges { public setEndDate(d: DlDateTimePickerChange) { this.updateEndDate(d.value); - if(this.endDateSignpost){ + if (this.endDateSignpost) { this.endDateSignpost.close(); } } diff --git a/src/app/session-statistics/session-time-statistics/session-time-statistics.component.html b/src/app/session-statistics/session-time-statistics/session-time-statistics.component.html new file mode 100644 index 00000000..8a9b7a93 --- /dev/null +++ b/src/app/session-statistics/session-time-statistics/session-time-statistics.component.html @@ -0,0 +1,54 @@ +
+
+
+ + + + +
+
+
+
+
+
+ + +
+
+
+ + Step + Average Duration + Sessions + + Step {{ i + 1 }} + {{ formatDuration(duration) }} + {{ getNumberOfSessionsForStep(i) }} + + + Total + {{ formatDuration(totalDuration) }} + {{ getNumberOfSessionsForStep(0) }} + + +
+
diff --git a/src/app/session-statistics/session-time-statistics/session-time-statistics.component.scss b/src/app/session-statistics/session-time-statistics/session-time-statistics.component.scss new file mode 100644 index 00000000..e69de29b diff --git a/src/app/session-statistics/session-time-statistics/session-time-statistics.component.ts b/src/app/session-statistics/session-time-statistics/session-time-statistics.component.ts new file mode 100644 index 00000000..8ede02b1 --- /dev/null +++ b/src/app/session-statistics/session-time-statistics/session-time-statistics.component.ts @@ -0,0 +1,265 @@ +import { Component, Input, OnInit, ViewChild } from '@angular/core'; +import { ChartConfiguration, ChartData, ChartEvent, ChartType } from 'chart.js'; +import { BaseChartDirective } from 'ng2-charts'; +import DataLabelsPlugin, { Context } from 'chartjs-plugin-datalabels'; +import { Progress } from '../../data/progress'; +import { ProgressService } from '../../data/progress.service'; +import { durationFormatter } from '../../utils'; +import { + FormControl, + FormGroup, + NonNullableFormBuilder, + Validators, +} from '@angular/forms'; +import { deepCopy } from 'src/app/deepcopy'; + +type ChartDetailsFormGroup = FormGroup<{ + scenario: FormControl; +}>; + +@Component({ + selector: 'app-session-time-statistics', + templateUrl: './session-time-statistics.component.html', + styleUrls: ['./session-time-statistics.component.scss'], +}) +export class SessionTimeStatisticsComponent implements OnInit { + // If no scheduledEvent is given, we display statistics about all progresses for a given time range + // If a scheduledEvent is given, we display statistics about all progresses from this scheduledEvent + @Input() + public progresses: Progress[]; + + @Input() + public scenariosWithSession: string[]; + + public selectedScenario: string; + + public stepDurations: number[]; + public stepCounts: number[]; + public avgDuration: number[]; + public totalDuration: number; + + public chartDetails: ChartDetailsFormGroup; + public barChartData: ChartData<'bar'> = { + labels: [], + datasets: [], + }; + public barChartOptions: ChartConfiguration['options'] = { + responsive: true, + // We use these empty structures as placeholders for dynamic theming. + scales: { + x: {}, + y: { + min: 0, + ticks: { + precision: 0, + stepSize: 60, + maxTicksLimit: 10, + callback: function (value: number, index, ticks) { + // Format y-axis labels to time string + return durationFormatter(value); + }, + }, + }, + }, + plugins: { + legend: { + display: true, + position: 'top', + }, + datalabels: { + // only display datalabels if != 0 + display: (context: Context) => { + return context.dataset.data[context.dataIndex] != 0; + }, + anchor: 'end', + align: 'end', + formatter: function (duration: number, context) { + return durationFormatter(duration); + }, + }, + }, + }; + public barChartType: ChartType = 'bar'; + public barChartPlugins = [ + DataLabelsPlugin, + { + id: 'legendMargin', + // chart is of type Chart<'bar'>. However, we are forced to use chart.js version 3.4.0 because it is used by ng2-charts as peer dependency. + // And the "legend" property is not defined in the type definitions of this version. + + // We can not upgrade ng2-charts (and so its peer dependency) yet because it requires Angular 14. + beforeInit: function (chart: any) { + // Get the reference to the original fit function + const originalFit = (chart.legend as any).fit; + + // Override the fit function + (chart.legend as any).fit = function fit() { + // Call original function and bind scope in order to use `this` correctly inside it + originalFit.bind(chart.legend)(); + // Change the height + this.height += 20; + }; + }, + }, + ]; + + constructor( + public progressService: ProgressService, + private _fb: NonNullableFormBuilder + ) {} + + ngOnInit(): void { + this.chartDetails = this._fb.group({ + scenario: this._fb.control(this.scenariosWithSession[0] ?? '', [ + Validators.required, + Validators.minLength(1), + ]), + }); + + this.selectedScenario = this.scenariosWithSession[0] ?? ''; + this.updateData(); + + this.chartDetails.controls.scenario.valueChanges.subscribe( + (scenario: string) => { + this.chartDetails.controls.scenario.setValue(scenario, { + emitEvent: false, + }); + this.selectedScenario = scenario; + this.updateData(); + } + ); + } + + @ViewChild(BaseChartDirective) chart: BaseChartDirective | undefined; + + public clearScenarios(): void { + this.chartDetails.controls.scenario.reset(); + } + + private updateData() { + let evaluatedProgressData: Progress[] = ( + deepCopy(this.progresses) + ); + evaluatedProgressData = evaluatedProgressData.filter( + (progress: Progress) => this.selectedScenario == progress.scenario_name + ); + + // TODO calculate data + let stepTime = []; + let stepProgressCount = []; + + evaluatedProgressData.forEach((p) => { + // Increase the count of progresses that have been to this step + for (let i = 0; i < p.max_step; i++) { + if (stepProgressCount[i]) { + stepProgressCount[i]++; + } else { + stepProgressCount[i] = 1; + } + } + + for (let i = 1; i < p.steps.length; i++) { + const step = p.steps[i].step; + let step_end_time; + if (i + 1 == p.steps.length) { + // This is the last step entry, we take the last update time to calculate the duration + step_end_time = p.last_update; + } else { + // Normal case is to take the next step entry + step_end_time = p.steps[i + 1].timestamp; + } + + let duration = + new Date(step_end_time).getTime() - + new Date(p.steps[i].timestamp).getTime(); + + if (stepTime[step - 1]) { + stepTime[step - 1] += duration; + } else { + stepTime[step - 1] = duration; + } + } + }); + + this.stepCounts = stepProgressCount; + this.stepDurations = stepTime; + this.avgDuration = []; + this.totalDuration = 0; + + for (let i = 0; i < this.stepDurations.length; i++) { + const avgStepDurationInSeconds = Math.round( + this.stepDurations[i] / this.stepCounts[i] / 1000 + ); + this.avgDuration.push(avgStepDurationInSeconds); + this.totalDuration += avgStepDurationInSeconds; + } + + this.processData(); + } + + private processData() { + this.prepareBarchartDatasets(); + this.updateBarchartData(); + this.chart?.update(); + } + + // events + public chartClicked({ + event, + active, + }: { + event?: ChartEvent; + active?: {}[]; + }): void { + // console.log(event, active); + } + + public chartHovered({ + event, + active, + }: { + event?: ChartEvent; + active?: {}[]; + }): void { + // console.log(event, active); + } + + private prepareBarchartDatasets() { + this.barChartData.datasets.length = 0; + this.barChartData.datasets.push({ + data: Array.from({ + length: this.barChartData.labels.length, + }).fill(0), + label: this.selectedScenario, + stack: 'a', + }); + } + + private updateBarchartData() { + if (this.barChartData.datasets.length === 0) { + // there are no scenarios selected and there is nothing to add ... so return! + return; + } + + this.barChartData.datasets[0].data = this.avgDuration; + + this.updateLabels(); + } + + // Info: Labels are not getting updated before calling this.chart.update() + private updateLabels() { + // We need to keep the same reference for our label array, otherwise label data is not updated correctly -> hence set length to 0 + this.barChartData.labels.length = 0; + for (let i = 0; i < this.avgDuration.length; i++) { + this.barChartData.labels.push('Step ' + (i + 1)); + } + } + + public formatDuration(duration: number) { + return durationFormatter(duration); + } + + public getNumberOfSessionsForStep(stepIndex: number) { + return this.stepCounts[stepIndex] ?? '-'; + } +} diff --git a/src/app/utils.ts b/src/app/utils.ts index 5df5f155..a9d6e7c4 100644 --- a/src/app/utils.ts +++ b/src/app/utils.ts @@ -1,29 +1,56 @@ -export function timeSince(date: Date, end: Date = new Date(), series: number = 2,) { - var seconds: number = Math.floor((end.getTime() - date.getTime()) / 1000); - var intervals = [31536000, 2592000, 86400, 3600, 60, 1]; - var intervalCounts = [0,0,0,0,0,0]; - var intervalStrings = ["year", "month", "day", "hour", "minute", "second"]; - var text = ""; +export function timeSince( + date: Date, + end: Date = new Date(), + series: number = 2 +) { + var seconds: number = Math.floor((end.getTime() - date.getTime()) / 1000); + var intervals = [31536000, 2592000, 86400, 3600, 60, 1]; + var intervalCounts = [0, 0, 0, 0, 0, 0]; + var intervalStrings = ['year', 'month', 'day', 'hour', 'minute', 'second']; + var text = ''; - for(let i = 0; i < intervals.length; i++){ - let interval = seconds / intervals[i]; - if (interval > 1) { - let count = Math.floor(interval) - intervalCounts[i] = count; - seconds -= count * intervals[i]; - continue; - } + for (let i = 0; i < intervals.length; i++) { + let interval = seconds / intervals[i]; + if (interval > 1) { + let count = Math.floor(interval); + intervalCounts[i] = count; + seconds -= count * intervals[i]; + continue; } + } - for(let i = 0; i < intervalCounts.length && series > 0; i++){ - if(intervalCounts[i] > 0 || i + 1 == intervalCounts.length){ - text += intervalCounts[i] + " " + intervalStrings[i] + (intervalCounts[i] != 1 ? "s" : ""); - series -= 1; - if(series > 0 && i + 1 != intervalCounts.length){ - text += ", "; - } + for (let i = 0; i < intervalCounts.length && series > 0; i++) { + if (intervalCounts[i] > 0 || i + 1 == intervalCounts.length) { + text += + intervalCounts[i] + + ' ' + + intervalStrings[i] + + (intervalCounts[i] != 1 ? 's' : ''); + series -= 1; + if (series > 0 && i + 1 != intervalCounts.length) { + text += ', '; } } + } + + return text; +} + +// Converts duration from a number to a text string +export function durationFormatter(duration: number) { + const hours = Math.floor(duration / 3600); + const minutes = Math.floor((duration % 3600) / 60); + const seconds = duration % 60; - return text; -} \ No newline at end of file + let result = ''; + if (hours > 0) { + result += `${hours}h`; + } + if (minutes > 0 || (hours > 0 && seconds > 0)) { + result += `${minutes}m`; + } + if (seconds > 0) { + result += `${seconds}s`; + } + return result || '0s'; +} From 1bed32639a8fc3903c844d6efe20b14a80018eba Mon Sep 17 00:00:00 2001 From: Jan-Gerrit Goebel Date: Tue, 7 May 2024 13:16:29 +0200 Subject: [PATCH 3/6] fix that clearing scenarios does not work --- src/app/session-statistics/session-statistics.component.ts | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/app/session-statistics/session-statistics.component.ts b/src/app/session-statistics/session-statistics.component.ts index 6cd4f2e6..a1119eff 100644 --- a/src/app/session-statistics/session-statistics.component.ts +++ b/src/app/session-statistics/session-statistics.component.ts @@ -218,7 +218,7 @@ export class SessionStatisticsComponent implements OnInit, OnChanges { } public clearScenarios(): void { - this.chartDetails.controls.scenarios.reset(); + this.chartDetails.controls.scenarios.setValue(['*']); } private validateStartDate(): ValidatorFn { @@ -447,7 +447,7 @@ export class SessionStatisticsComponent implements OnInit, OnChanges { private allScenariosSelected(): boolean { const selectedScenarios = this.chartDetails.controls.scenarios.value; - return selectedScenarios.length === 1 && selectedScenarios[0] === '*'; + return !selectedScenarios || selectedScenarios.length === 1 && selectedScenarios[0] === '*'; } private prepareBarchartDatasets() { From 799fce83236068d95524c6676d69c14c6197ecd3 Mon Sep 17 00:00:00 2001 From: Jan-Gerrit Goebel Date: Tue, 7 May 2024 14:06:24 +0200 Subject: [PATCH 4/6] Rework to use map instead of list --- .../session-statistics.component.html | 12 ++++---- .../session-statistics.component.ts | 30 ++++++++++++------- .../session-time-statistics.component.html | 7 +++-- .../session-time-statistics.component.ts | 22 +++++++++----- 4 files changed, 45 insertions(+), 26 deletions(-) diff --git a/src/app/session-statistics/session-statistics.component.html b/src/app/session-statistics/session-statistics.component.html index 70bdc1a8..02155ea4 100644 --- a/src/app/session-statistics/session-statistics.component.html +++ b/src/app/session-statistics/session-statistics.component.html @@ -28,10 +28,12 @@

Session Statistics

- {{ scenario }} + {{ scenario.value }} @@ -165,7 +167,7 @@

Started Sessions (List)

- {{ item.key }} + {{ getScenarioName(item.key) }} {{ item.value }} @@ -176,7 +178,7 @@

Scenario Statistics

diff --git a/src/app/session-statistics/session-statistics.component.ts b/src/app/session-statistics/session-statistics.component.ts index a1119eff..df005532 100644 --- a/src/app/session-statistics/session-statistics.component.ts +++ b/src/app/session-statistics/session-statistics.component.ts @@ -106,6 +106,7 @@ export class SessionStatisticsComponent implements OnInit, OnChanges { }, ]; public scenariosWithSession: string[] = []; + public scenariosWithSessionMap: Map = new Map(); // Maps the id to the name public totalSessionsPerScenario: Map = new Map(); public descSort = ClrDatagridSortOrder.DESC; @@ -121,7 +122,7 @@ export class SessionStatisticsComponent implements OnInit, OnChanges { ) { this.currentScheduledEvent = this.scheduledEvent; this.progressesCache = null; // Reset cache so data from the changed SE can be retreived - this.scenariosWithSession = []; + this.scenariosWithSessionMap = new Map(); this.totalSessionsPerScenario = new Map(); this.chartDetails.controls.scenarios.setValue(['*']); this.setDatesToScheduledEvent( @@ -439,22 +440,23 @@ export class SessionStatisticsComponent implements OnInit, OnChanges { private setupScenariosWithSessions(progressData: Progress[]) { this.scenariosWithSession = []; progressData.forEach((prog: Progress) => { - if (!this.scenariosWithSession.includes(prog.scenario_name)) { - this.scenariosWithSession.push(prog.scenario_name); - } + this.scenariosWithSessionMap.set(prog.scenario, prog.scenario_name); }); } private allScenariosSelected(): boolean { const selectedScenarios = this.chartDetails.controls.scenarios.value; - return !selectedScenarios || selectedScenarios.length === 1 && selectedScenarios[0] === '*'; + return ( + !selectedScenarios || + (selectedScenarios.length === 1 && selectedScenarios[0] === '*') + ); } private prepareBarchartDatasets() { this.barChartData.datasets.length = 0; const selectedScenarios = this.chartDetails.controls.scenarios.value; if (this.allScenariosSelected()) { - this.scenariosWithSession.forEach((sWithSession: string) => { + this.scenariosWithSessionMap.forEach((sWithSession: string) => { this.barChartData.datasets.push({ data: Array.from({ length: this.barChartData.labels.length, @@ -506,16 +508,18 @@ export class SessionStatisticsComponent implements OnInit, OnChanges { return; } let evaluatedProgressData: Progress[] = progressData; - let selectedScenarios: string[] = this.scenariosWithSession; + let selectedScenarios: string[] = Array.from( + this.scenariosWithSessionMap.keys() + ); if (!this.allScenariosSelected()) { selectedScenarios = this.chartDetails.controls.scenarios.value; evaluatedProgressData = progressData.filter((progress: Progress) => - selectedScenarios.includes(progress.scenario_name) + selectedScenarios.includes(progress.scenario) ); } evaluatedProgressData.forEach((prog: Progress) => { const index = getIndex(prog); - (this.barChartData.datasets[selectedScenarios.indexOf(prog.scenario_name)] + (this.barChartData.datasets[selectedScenarios.indexOf(prog.scenario)] .data[index] as number) += 1; }); } @@ -524,8 +528,8 @@ export class SessionStatisticsComponent implements OnInit, OnChanges { this.totalSessionsPerScenario.clear(); this.totalSessionsPerScenario = progressData.reduce( (totalSessions, progress) => { - const partialSum = totalSessions.get(progress.scenario_name) ?? 0; - totalSessions.set(progress.scenario_name, partialSum + 1); + const partialSum = totalSessions.get(progress.scenario) ?? 0; + totalSessions.set(progress.scenario, partialSum + 1); return totalSessions; }, new Map() @@ -583,4 +587,8 @@ export class SessionStatisticsComponent implements OnInit, OnChanges { this.barChartData.labels.push(labelDateString); } } + + public getScenarioName(scenarioId: string) { + return this.scenariosWithSessionMap.get(scenarioId) ?? scenarioId; + } } diff --git a/src/app/session-statistics/session-time-statistics/session-time-statistics.component.html b/src/app/session-statistics/session-time-statistics/session-time-statistics.component.html index 8a9b7a93..6a9052d6 100644 --- a/src/app/session-statistics/session-time-statistics/session-time-statistics.component.html +++ b/src/app/session-statistics/session-time-statistics/session-time-statistics.component.html @@ -7,8 +7,11 @@ diff --git a/src/app/session-statistics/session-time-statistics/session-time-statistics.component.ts b/src/app/session-statistics/session-time-statistics/session-time-statistics.component.ts index 8ede02b1..da777ec1 100644 --- a/src/app/session-statistics/session-time-statistics/session-time-statistics.component.ts +++ b/src/app/session-statistics/session-time-statistics/session-time-statistics.component.ts @@ -29,7 +29,7 @@ export class SessionTimeStatisticsComponent implements OnInit { public progresses: Progress[]; @Input() - public scenariosWithSession: string[]; + public scenariosWithSessionMap: Map; //Maps the scenario id to the name public selectedScenario: string; @@ -109,14 +109,16 @@ export class SessionTimeStatisticsComponent implements OnInit { ) {} ngOnInit(): void { + const firstScenario = + this.scenariosWithSessionMap?.keys().next().value ?? ''; this.chartDetails = this._fb.group({ - scenario: this._fb.control(this.scenariosWithSession[0] ?? '', [ + scenario: this._fb.control(firstScenario, [ Validators.required, Validators.minLength(1), ]), }); - this.selectedScenario = this.scenariosWithSession[0] ?? ''; + this.selectedScenario = firstScenario; this.updateData(); this.chartDetails.controls.scenario.valueChanges.subscribe( @@ -137,17 +139,17 @@ export class SessionTimeStatisticsComponent implements OnInit { } private updateData() { - let evaluatedProgressData: Progress[] = ( - deepCopy(this.progresses) - ); - evaluatedProgressData = evaluatedProgressData.filter( - (progress: Progress) => this.selectedScenario == progress.scenario_name + console.log(this.progresses); + const evaluatedProgressData = this.progresses.filter( + (progress: Progress) => this.selectedScenario == progress.scenario ); // TODO calculate data let stepTime = []; let stepProgressCount = []; + console.log(evaluatedProgressData); + evaluatedProgressData.forEach((p) => { // Increase the count of progresses that have been to this step for (let i = 0; i < p.max_step; i++) { @@ -194,6 +196,10 @@ export class SessionTimeStatisticsComponent implements OnInit { this.totalDuration += avgStepDurationInSeconds; } + console.log(this.stepCounts); + console.log(this.stepDurations); + console.log(this.avgDuration); + this.processData(); } From cc6de5840bc278ce29b511db9b1504bf283f9b6b Mon Sep 17 00:00:00 2001 From: Jan-Gerrit Goebel Date: Tue, 7 May 2024 14:14:11 +0200 Subject: [PATCH 5/6] Fix labels to display scenario name --- .../session-statistics/session-statistics.component.html | 4 ++-- src/app/session-statistics/session-statistics.component.ts | 7 +++---- 2 files changed, 5 insertions(+), 6 deletions(-) diff --git a/src/app/session-statistics/session-statistics.component.html b/src/app/session-statistics/session-statistics.component.html index 02155ea4..772ecc8c 100644 --- a/src/app/session-statistics/session-statistics.component.html +++ b/src/app/session-statistics/session-statistics.component.html @@ -23,8 +23,8 @@

Session Statistics

name="scenarios" clrMulti="true" > - - {{ scenarioSelected }} + + {{ getScenarioName(scenario) }} = new Map(); // Maps the id to the name public totalSessionsPerScenario: Map = new Map(); public descSort = ClrDatagridSortOrder.DESC; @@ -438,7 +437,7 @@ export class SessionStatisticsComponent implements OnInit, OnChanges { } private setupScenariosWithSessions(progressData: Progress[]) { - this.scenariosWithSession = []; + this.scenariosWithSessionMap = new Map(); progressData.forEach((prog: Progress) => { this.scenariosWithSessionMap.set(prog.scenario, prog.scenario_name); }); @@ -461,7 +460,7 @@ export class SessionStatisticsComponent implements OnInit, OnChanges { data: Array.from({ length: this.barChartData.labels.length, }).fill(0), - label: sWithSession, + label: this.getScenarioName(sWithSession), stack: 'a', }); }); @@ -471,7 +470,7 @@ export class SessionStatisticsComponent implements OnInit, OnChanges { data: Array.from({ length: this.barChartData.labels.length, }).fill(0), - label: sWithSession, + label: this.getScenarioName(sWithSession), stack: 'a', }); }); From cc87f39ad5faeb153089d1f353e7b302b5d015e3 Mon Sep 17 00:00:00 2001 From: Jan-Gerrit Goebel Date: Tue, 7 May 2024 14:16:34 +0200 Subject: [PATCH 6/6] remove logs and fix another label --- .../session-time-statistics.component.ts | 8 +------- 1 file changed, 1 insertion(+), 7 deletions(-) diff --git a/src/app/session-statistics/session-time-statistics/session-time-statistics.component.ts b/src/app/session-statistics/session-time-statistics/session-time-statistics.component.ts index da777ec1..d3d2750b 100644 --- a/src/app/session-statistics/session-time-statistics/session-time-statistics.component.ts +++ b/src/app/session-statistics/session-time-statistics/session-time-statistics.component.ts @@ -148,8 +148,6 @@ export class SessionTimeStatisticsComponent implements OnInit { let stepTime = []; let stepProgressCount = []; - console.log(evaluatedProgressData); - evaluatedProgressData.forEach((p) => { // Increase the count of progresses that have been to this step for (let i = 0; i < p.max_step; i++) { @@ -196,10 +194,6 @@ export class SessionTimeStatisticsComponent implements OnInit { this.totalDuration += avgStepDurationInSeconds; } - console.log(this.stepCounts); - console.log(this.stepDurations); - console.log(this.avgDuration); - this.processData(); } @@ -236,7 +230,7 @@ export class SessionTimeStatisticsComponent implements OnInit { data: Array.from({ length: this.barChartData.labels.length, }).fill(0), - label: this.selectedScenario, + label: this.scenariosWithSessionMap.get(this.selectedScenario), stack: 'a', }); }