mirror of
https://github.com/joaovitoriasilva/endurain.git
synced 2026-01-07 23:13:57 -05:00
Add time duration display to heart rate zones
- Backend: Calculate time_seconds for each HR zone based on waypoint ratio - Frontend: Display both percentage and time (e.g., '25% (15m)') in HR zone charts - Update BarChartComponent to support timeSeconds prop - Use activity total_timer_time for accurate time calculation
This commit is contained in:
@@ -424,6 +424,15 @@ def transform_activity_streams_hr(activity_stream, activity, db):
|
||||
np.sum(hr_values >= zone_4),
|
||||
]
|
||||
zone_percentages = [round((count / total) * 100, 2) for count in zone_counts]
|
||||
|
||||
# Calculate time in seconds for each zone
|
||||
# Use the same logic as percentage: distribute total_timer_time based on waypoint ratio
|
||||
if hasattr(activity, 'total_timer_time') and activity.total_timer_time and total > 0:
|
||||
total_time_seconds = activity.total_timer_time
|
||||
zone_time_seconds = [int((count / total) * total_time_seconds) for count in zone_counts]
|
||||
else:
|
||||
# Fallback: assume waypoints represent equal time intervals
|
||||
zone_time_seconds = [int(count) for count in zone_counts]
|
||||
|
||||
# Calculate zone HR boundaries for display
|
||||
zone_hr = {
|
||||
@@ -434,11 +443,11 @@ def transform_activity_streams_hr(activity_stream, activity, db):
|
||||
"zone_5": f">= {int(zone_4)}",
|
||||
}
|
||||
activity_stream.hr_zone_percentages = {
|
||||
"zone_1": {"percent": zone_percentages[0], "hr": zone_hr["zone_1"]},
|
||||
"zone_2": {"percent": zone_percentages[1], "hr": zone_hr["zone_2"]},
|
||||
"zone_3": {"percent": zone_percentages[2], "hr": zone_hr["zone_3"]},
|
||||
"zone_4": {"percent": zone_percentages[3], "hr": zone_hr["zone_4"]},
|
||||
"zone_5": {"percent": zone_percentages[4], "hr": zone_hr["zone_5"]},
|
||||
"zone_1": {"percent": zone_percentages[0], "hr": zone_hr["zone_1"], "time_seconds": zone_time_seconds[0]},
|
||||
"zone_2": {"percent": zone_percentages[1], "hr": zone_hr["zone_2"], "time_seconds": zone_time_seconds[1]},
|
||||
"zone_3": {"percent": zone_percentages[2], "hr": zone_hr["zone_3"], "time_seconds": zone_time_seconds[2]},
|
||||
"zone_4": {"percent": zone_percentages[3], "hr": zone_hr["zone_4"], "time_seconds": zone_time_seconds[3]},
|
||||
"zone_5": {"percent": zone_percentages[4], "hr": zone_hr["zone_5"], "time_seconds": zone_time_seconds[4]},
|
||||
}
|
||||
|
||||
return activity_stream
|
||||
|
||||
@@ -141,7 +141,16 @@
|
||||
:labels="getHrBarChartData(hrZones, t).labels"
|
||||
:values="getHrBarChartData(hrZones, t).values"
|
||||
:barColors="getHrBarChartData(hrZones, t).barColors"
|
||||
:datalabelsFormatter="(value) => `${Math.round(value)}%`"
|
||||
:timeSeconds="getHrBarChartData(hrZones, t).timeSeconds"
|
||||
:datalabelsFormatter="
|
||||
(value, context) => {
|
||||
const timeSeconds = getHrBarChartData(hrZones, t).timeSeconds[context.dataIndex]
|
||||
const hours = Math.floor(timeSeconds / 3600)
|
||||
const minutes = Math.floor((timeSeconds % 3600) / 60)
|
||||
const timeStr = hours > 0 ? `${hours}h ${minutes}m` : `${minutes}m`
|
||||
return `${Math.round(value)}% (${timeStr})`
|
||||
}
|
||||
"
|
||||
:title="$t('activityMandAbovePillsComponent.labelHRZones')"
|
||||
/>
|
||||
<hr />
|
||||
|
||||
@@ -123,7 +123,16 @@
|
||||
:labels="getHrBarChartData(hrZones, t).labels"
|
||||
:values="getHrBarChartData(hrZones, t).values"
|
||||
:barColors="getHrBarChartData(hrZones, t).barColors"
|
||||
:datalabelsFormatter="(value) => `${Math.round(value)}%`"
|
||||
:timeSeconds="getHrBarChartData(hrZones, t).timeSeconds"
|
||||
:datalabelsFormatter="
|
||||
(value, context) => {
|
||||
const timeSeconds = getHrBarChartData(hrZones, t).timeSeconds[context.dataIndex]
|
||||
const hours = Math.floor(timeSeconds / 3600)
|
||||
const minutes = Math.floor((timeSeconds % 3600) / 60)
|
||||
const timeStr = hours > 0 ? `${hours}h ${minutes}m` : `${minutes}m`
|
||||
return `${Math.round(value)}% (${timeStr})`
|
||||
}
|
||||
"
|
||||
:title="$t('activityMandAbovePillsComponent.labelHRZones')"
|
||||
/>
|
||||
</div>
|
||||
|
||||
@@ -31,6 +31,10 @@ const props = defineProps({
|
||||
datalabelsFormatter: {
|
||||
type: Function,
|
||||
default: null
|
||||
},
|
||||
timeSeconds: {
|
||||
type: Array,
|
||||
default: () => []
|
||||
}
|
||||
})
|
||||
|
||||
|
||||
@@ -1,3 +1,17 @@
|
||||
/**
|
||||
* Formats seconds into a human-readable duration string.
|
||||
* @param {number} seconds - The total number of seconds to format.
|
||||
* @returns {string} Formatted duration string (e.g., "2h 30m" or "45m").
|
||||
*/
|
||||
function formatDuration(seconds) {
|
||||
const hours = Math.floor(seconds / 3600)
|
||||
const minutes = Math.floor((seconds % 3600) / 60)
|
||||
if (hours > 0) {
|
||||
return `${hours}h ${minutes}m`
|
||||
}
|
||||
return `${minutes}m`
|
||||
}
|
||||
|
||||
export function getZoneColor(index) {
|
||||
// Example colors for 5 HR zones
|
||||
const colors = [
|
||||
@@ -18,6 +32,7 @@ export function getHrBarChartData(hrZones, t) {
|
||||
),
|
||||
// values: zones.map(z => `${z.percent ?? 0}%`),
|
||||
values: zones.map((z) => z.percent ?? 0),
|
||||
barColors: zones.map((_, i) => getZoneColor(i))
|
||||
barColors: zones.map((_, i) => getZoneColor(i)),
|
||||
timeSeconds: zones.map((z) => z.time_seconds ?? 0)
|
||||
}
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user