Skip to content

Commit d07b72f

Browse files
committed
Show statistics as stacked bar charts
Also making sure we include all activities for the past three months and cleaning up some code styling.
1 parent ec39a2c commit d07b72f

File tree

4 files changed

+100
-78
lines changed

4 files changed

+100
-78
lines changed

Tevling/Pages/Statistics.razor

Lines changed: 7 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -2,11 +2,15 @@
22
<PageTitle>Tevling - Statistics</PageTitle>
33

44
<h1 class="title mb-5">Statistics</h1>
5-
<div class="d-flex flex-wrap p-2" >
5+
6+
<div class="d-flex flex-wrap p-2">
67
<canvas id="totalDistanceChart"></canvas>
78
</div>
8-
<div class="d-flex flex-wrap p-2" >
9+
10+
<div class="d-flex flex-wrap p-2">
911
<canvas id="totalElevationChart"></canvas>
10-
</div><div class="d-flex flex-wrap p-2" >
12+
</div>
13+
14+
<div class="d-flex flex-wrap p-2">
1115
<canvas id="totalTimeChart"></canvas>
1216
</div>

Tevling/Pages/Statistics.razor.cs

Lines changed: 44 additions & 41 deletions
Original file line numberDiff line numberDiff line change
@@ -17,7 +17,7 @@ protected override async Task OnInitializedAsync()
1717
{
1818
_athlete = await AuthenticationService.GetCurrentAthleteAsync();
1919

20-
ActivityFilter filter = new(_athlete.Id, false, DateTimeOffset.Now.AddMonths(-2));
20+
ActivityFilter filter = new(_athlete.Id, false, DateTimeOffset.Now.AddMonths(-2).ToFirstOfTheMonth());
2121
_activities = await ActivityService.GetActivitiesAsync(filter);
2222
}
2323

@@ -38,59 +38,62 @@ private Dictionary<string, float[]> GetAggregatedMeasurementData(Func<Activity,
3838
.Sum(selector);
3939
})
4040
.ToArray()
41-
);
41+
)
42+
.Where(d => d.Value.Length > 0)
43+
.ToDictionary();
4244

43-
if (aggregatedData.Count != 0)
44-
{
45-
aggregatedData["Total"] =
46-
[
47-
.. aggregatedData.Values.Aggregate((sum, next) => [.. sum.Zip(next, (a, b) => a + b)]),
48-
];
49-
}
45+
// if (aggregatedData.Count != 0)
46+
// {
47+
// aggregatedData["Total"] =
48+
// [
49+
// .. aggregatedData.Values.Aggregate((sum, next) => [.. sum.Zip(next, (a, b) => a + b)]),
50+
// ];
51+
// }
5052

5153
return aggregatedData;
5254
}
5355

5456
protected override async Task OnAfterRenderAsync(bool firstRender)
5557
{
56-
if (firstRender)
57-
{
58-
_module = await Js.InvokeAsync<IJSObjectReference>("import", "./Pages/Statistics.razor.js");
58+
if (!firstRender)
59+
return;
60+
61+
_module = await Js.InvokeAsync<IJSObjectReference>("import", "./Pages/Statistics.razor.js");
5962

60-
List<int> lastThreeMonths =
61-
[
63+
string[] lastThreeMonths = new int[]
64+
{
6265
DateTimeOffset.Now.AddMonths(-2).Month,
6366
DateTimeOffset.Now.AddMonths(-1).Month,
6467
DateTimeOffset.Now.Month,
65-
];
66-
67-
Dictionary<string, float[]> distancesLastThreeMonths =
68-
GetAggregatedMeasurementData(a => a.Details.DistanceInMeters);
69-
Dictionary<string, float[]> elevationLastThreeMonths =
70-
GetAggregatedMeasurementData(a => a.Details.TotalElevationGain);
71-
Dictionary<string, float[]> timeLastThreeMonths =
72-
GetAggregatedMeasurementData(a => (float)a.Details.MovingTimeInSeconds / 3600);
68+
}
69+
.Select(CultureInfo.CurrentCulture.DateTimeFormat.GetMonthName)
70+
.ToArray();
7371

72+
Dictionary<string, float[]> distanceLastThreeMonths =
73+
GetAggregatedMeasurementData(a => (float)a.Details.DistanceInMeters / 1000);
74+
Dictionary<string, float[]> elevationLastThreeMonths =
75+
GetAggregatedMeasurementData(a => a.Details.TotalElevationGain);
76+
Dictionary<string, float[]> timeLastThreeMonths =
77+
GetAggregatedMeasurementData(a => (float)a.Details.MovingTimeInSeconds / 3600);
7478

75-
await _module.InvokeVoidAsync(
76-
"drawChart",
77-
distancesLastThreeMonths,
78-
lastThreeMonths.Select(m => CultureInfo.CurrentCulture.DateTimeFormat.GetMonthName(m)).ToList(),
79-
"totalDistanceChart",
80-
"Total Distance [m]");
81-
await _module.InvokeVoidAsync(
82-
"drawChart",
83-
elevationLastThreeMonths,
84-
lastThreeMonths.Select(m => CultureInfo.CurrentCulture.DateTimeFormat.GetMonthName(m)).ToList(),
85-
"totalElevationChart",
86-
"Total Elevation [m]");
87-
await _module.InvokeVoidAsync(
88-
"drawChart",
89-
timeLastThreeMonths,
90-
lastThreeMonths.Select(m => CultureInfo.CurrentCulture.DateTimeFormat.GetMonthName(m)).ToList(),
91-
"totalTimeChart",
92-
"Total Time [h]");
93-
}
79+
await _module.InvokeVoidAsync(
80+
"drawChart",
81+
distanceLastThreeMonths,
82+
lastThreeMonths,
83+
"totalDistanceChart",
84+
"Total Distance [km]");
85+
await _module.InvokeVoidAsync(
86+
"drawChart",
87+
elevationLastThreeMonths,
88+
lastThreeMonths,
89+
"totalElevationChart",
90+
"Total Elevation [m]");
91+
await _module.InvokeVoidAsync(
92+
"drawChart",
93+
timeLastThreeMonths,
94+
lastThreeMonths,
95+
"totalTimeChart",
96+
"Total Time [h]");
9497
}
9598

9699
public async ValueTask DisposeAsync()

Tevling/Pages/Statistics.razor.js

Lines changed: 43 additions & 34 deletions
Original file line numberDiff line numberDiff line change
@@ -1,38 +1,47 @@
1-
export function drawChart (activityData, labels, chartName, chartTitle) {
2-
var ctx = document.getElementById(chartName).getContext('2d');
3-
var canvas = document.getElementById(chartName);
1+
export function drawChart(activityData, labels, chartName, chartTitle) {
2+
var ctx = document.getElementById(chartName).getContext("2d");
43

5-
// Destroy existing chart instance if it exists
6-
if (window[chartName] instanceof Chart) {
7-
window[chartName].destroy();
8-
}
4+
// Destroy existing chart instance if it exists
5+
if (window[chartName] instanceof Chart) {
6+
window[chartName].destroy();
7+
}
98

10-
let datasets = [];
9+
let datasets = [];
1110

12-
for (const [key, value] of Object.entries(activityData)) {
13-
datasets.push({
14-
label: key, data: value
15-
});
16-
}
17-
18-
window[chartName] = new Chart(ctx, {
19-
type: 'line',
20-
data: {
21-
labels: labels, datasets: datasets
22-
}, options: {
23-
plugins: {
24-
title: {
25-
display: true, text: chartTitle, color: 'rgba(54, 162, 235, 1)', font: {
26-
size: 20
27-
},
28-
},
29-
}, responsive: true, scales: {
30-
y: {
31-
beginAtZero: true,
32-
33-
}
34-
},
35-
36-
}
11+
for (const [key, value] of Object.entries(activityData)) {
12+
datasets.push({
13+
label: key,
14+
data: value,
3715
});
38-
};
16+
}
17+
18+
window[chartName] = new Chart(ctx, {
19+
type: "bar",
20+
data: {
21+
labels: labels,
22+
datasets: datasets,
23+
},
24+
options: {
25+
plugins: {
26+
title: {
27+
display: true,
28+
text: chartTitle,
29+
color: "rgba(54, 162, 235, 1)",
30+
font: {
31+
size: 20,
32+
},
33+
},
34+
},
35+
responsive: true,
36+
scales: {
37+
x: {
38+
stacked: true,
39+
},
40+
y: {
41+
stacked: true,
42+
beginAtZero: true,
43+
},
44+
},
45+
},
46+
});
47+
}

Tevling/Utils/DateTimeOffsetExtensions.cs

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -46,4 +46,10 @@ public static bool IsSameYearUtc(this DateTimeOffset dto)
4646
{
4747
return dto.UtcDateTime.Year == DateTimeOffset.UtcNow.Year;
4848
}
49+
50+
public static DateTimeOffset ToFirstOfTheMonth(this DateTimeOffset dto)
51+
{
52+
DateTimeOffset firstOfTheMonth = new(dto.Year, dto.Month, 1, 0, 0, 0, dto.Offset);
53+
return firstOfTheMonth;
54+
}
4955
}

0 commit comments

Comments
 (0)