Skip to content

Commit 2a158b2

Browse files
authored
Merge pull request #2 from lennon101/copilot/update-lap-bar-plot-widths
Make lap pace bar heights proportional to moving time
2 parents 2c1a732 + 778a7f9 commit 2a158b2

6 files changed

Lines changed: 101 additions & 11 deletions

File tree

Lines changed: 38 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,38 @@
1+
<?php
2+
3+
declare(strict_types=1);
4+
5+
namespace DoctrineMigrations;
6+
7+
use Doctrine\DBAL\Schema\Schema;
8+
use Doctrine\Migrations\AbstractMigration;
9+
10+
/**
11+
* Auto-generated Migration: Please modify to your needs!
12+
*/
13+
final class Version20260121012500 extends AbstractMigration
14+
{
15+
public function getDescription(): string
16+
{
17+
return 'Add minMovingTimeInSeconds and maxMovingTimeInSeconds to ActivityLap';
18+
}
19+
20+
public function up(Schema $schema): void
21+
{
22+
// this up() migration is auto-generated, please modify it to your needs
23+
// We need to delete existing laps because we're adding new required columns
24+
// and there's no way to calculate historical min/max moving times
25+
$this->addSql(<<<'SQL'
26+
DELETE FROM ActivityLap
27+
SQL);
28+
$this->addSql('ALTER TABLE ActivityLap ADD COLUMN minMovingTimeInSeconds INTEGER NOT NULL');
29+
$this->addSql('ALTER TABLE ActivityLap ADD COLUMN maxMovingTimeInSeconds INTEGER NOT NULL');
30+
}
31+
32+
public function down(Schema $schema): void
33+
{
34+
// this down() migration is auto-generated, please modify it to your needs
35+
$this->addSql('ALTER TABLE ActivityLap DROP COLUMN minMovingTimeInSeconds');
36+
$this->addSql('ALTER TABLE ActivityLap DROP COLUMN maxMovingTimeInSeconds');
37+
}
38+
}

src/Application/Import/ProcessRawActivityData/Pipeline/ProcessActivityLaps.php

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -50,6 +50,8 @@ public function process(OutputInterface $output): void
5050
maxSpeed: MetersPerSecond::from($lap['max_speed']),
5151
elevationDifference: Meter::from($lap['total_elevation_gain'] ?? 0),
5252
averageHeartRate: !empty($lap['average_heartrate']) ? (int) round($lap['average_heartrate']) : null,
53+
minMovingTimeInSeconds: $lap['min_moving_time'],
54+
maxMovingTimeInSeconds: $lap['max_moving_time'],
5355
));
5456
++$countLapsAdded;
5557
}

src/Domain/Activity/ActivityWithRawData.php

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -98,11 +98,15 @@ public function getLaps(): array
9898
{
9999
/** @var non-empty-array<float> $averageSpeeds */
100100
$averageSpeeds = array_column($this->rawData['laps'] ?? [], 'average_speed');
101+
/** @var non-empty-array<int> $movingTimes */
102+
$movingTimes = array_column($this->rawData['laps'] ?? [], 'moving_time');
101103

102104
return array_map(
103105
fn (array $split): array => array_merge($split, [
104106
'min_average_speed' => min($averageSpeeds),
105107
'max_average_speed' => max($averageSpeeds),
108+
'min_moving_time' => min($movingTimes),
109+
'max_moving_time' => max($movingTimes),
106110
]),
107111
$this->rawData['laps'] ?? [],
108112
);

src/Domain/Activity/Lap/ActivityLap.php

Lines changed: 37 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -47,6 +47,10 @@ private function __construct(
4747
private Meter $elevationDifference,
4848
#[ORM\Column(type: 'integer', nullable: true)]
4949
private ?int $averageHeartRate,
50+
#[ORM\Column(type: 'integer')]
51+
private int $minMovingTimeInSeconds,
52+
#[ORM\Column(type: 'integer')]
53+
private int $maxMovingTimeInSeconds,
5054
) {
5155
}
5256

@@ -64,6 +68,8 @@ public static function create(
6468
MetersPerSecond $maxSpeed,
6569
Meter $elevationDifference,
6670
?int $averageHeartRate,
71+
int $minMovingTimeInSeconds,
72+
int $maxMovingTimeInSeconds,
6773
): self {
6874
return new self(
6975
lapId: $lapId,
@@ -79,6 +85,8 @@ public static function create(
7985
maxSpeed: $maxSpeed,
8086
elevationDifference: $elevationDifference,
8187
averageHeartRate: $averageHeartRate,
88+
minMovingTimeInSeconds: $minMovingTimeInSeconds,
89+
maxMovingTimeInSeconds: $maxMovingTimeInSeconds,
8290
);
8391
}
8492

@@ -96,6 +104,8 @@ public static function fromState(
96104
MetersPerSecond $maxSpeed,
97105
Meter $elevationDifference,
98106
?int $averageHeartRate,
107+
int $minMovingTimeInSeconds,
108+
int $maxMovingTimeInSeconds,
99109
): self {
100110
return new self(
101111
lapId: $lapId,
@@ -111,6 +121,8 @@ public static function fromState(
111121
maxSpeed: $maxSpeed,
112122
elevationDifference: $elevationDifference,
113123
averageHeartRate: $averageHeartRate,
124+
minMovingTimeInSeconds: $minMovingTimeInSeconds,
125+
maxMovingTimeInSeconds: $maxMovingTimeInSeconds,
114126
);
115127
}
116128

@@ -197,6 +209,21 @@ public function getRelativePacePercentage(): float
197209
return round($relative, 2);
198210
}
199211

212+
public function getRelativeMovingTimePercentage(): float
213+
{
214+
$maxMovingTime = $this->getMaxMovingTimeInSeconds();
215+
216+
if ($maxMovingTime === 0) {
217+
return 0.0;
218+
}
219+
220+
$movingTime = $this->getMovingTimeInSeconds();
221+
$relative = ($movingTime / $maxMovingTime) * 100;
222+
$relative = max(0, min(100, $relative));
223+
224+
return round($relative, 2);
225+
}
226+
200227
public function getMinAverageSpeed(): MetersPerSecond
201228
{
202229
return $this->minAverageSpeed;
@@ -222,6 +249,16 @@ public function getAverageHeartRate(): ?int
222249
return $this->averageHeartRate;
223250
}
224251

252+
public function getMinMovingTimeInSeconds(): int
253+
{
254+
return $this->minMovingTimeInSeconds;
255+
}
256+
257+
public function getMaxMovingTimeInSeconds(): int
258+
{
259+
return $this->maxMovingTimeInSeconds;
260+
}
261+
225262
/**
226263
* @return array<string, mixed>
227264
*/

templates/html/activity/activity--sport-type--run.html.twig

Lines changed: 14 additions & 11 deletions
Original file line numberDiff line numberDiff line change
@@ -131,33 +131,36 @@
131131
<div class="w-32 px-2 py-2">{{ "HR"|trans }}</div>
132132
</div>
133133
{% for lap in laps %}
134-
<div class="flex items-center border-b last:border-b-0">
135-
<div class="w-8 px-2 py-2 font-bold">
134+
{% set heightPercentage = lap.getRelativeMovingTimePercentage() %}
135+
{% set maxBarHeight = 50 %}
136+
{% set calculatedBarHeight = (maxBarHeight * heightPercentage / 100)|round(1) %}
137+
<div class="flex items-center" style="margin-bottom: 3px;">
138+
<div class="w-8 px-2 py-1 font-bold">
136139
{{ lap.getLapNumber() }}
137140
</div>
138-
<div class="w-32 px-2 py-2">
141+
<div class="w-32 px-2 py-1">
139142
{{ lap.getDistanceInKilometer()|renderMeasurement(1) }}
140143
</div>
141-
<div class="w-32 px-2 py-2">
144+
<div class="w-32 px-2 py-1">
142145
{{ lap.getMovingTimeFormatted() }}
143146
</div>
144-
<div class="w-32 px-2 py-2">
147+
<div class="w-32 px-2 py-1">
145148
{{ lap.getPaceInSecPerKm()|formatPace }}
146149
</div>
147-
<div class="grow min-w-40 px-2 py-2">
148-
<div class=" bg-gray-200 rounded-full h-2.5">
149-
<div class="bg-strava-orange h-2.5 rounded-full" style="width: {{ lap.getRelativePacePercentage() }}%"></div>
150+
<div class="grow min-w-40 px-2 py-1">
151+
<div class="bg-gray-200 rounded" style="height: {{ calculatedBarHeight }}px;">
152+
<div class="bg-strava-orange rounded" style="width: {{ lap.getRelativePacePercentage() }}%; height: {{ calculatedBarHeight }}px;"></div>
150153
</div>
151154
</div>
152-
<div class="w-32 px-2 py-2">
155+
<div class="w-32 px-2 py-1">
153156
{{ lap.getElevationDifference()|renderMeasurement(0) }}
154157
</div>
155158
{% if lap.getAverageHeartRate() %}
156-
<div class="w-32 px-2 py-2">
159+
<div class="w-32 px-2 py-1">
157160
{{ lap.getAverageHeartRate() }}
158161
</div>
159162
{% else %}
160-
<div class="w-32 px-2 py-2">
163+
<div class="w-32 px-2 py-1">
161164
{{ "n/a"|trans }}
162165
</div>
163166
{% endif %}

tests/Domain/Activity/Lap/ActivityLapBuilder.php

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -25,6 +25,8 @@ final class ActivityLapBuilder
2525
private MetersPerSecond $maxSpeed;
2626
private readonly Meter $elevationDifference;
2727
private readonly ?int $averageHeartRate;
28+
private readonly int $minMovingTimeInSeconds;
29+
private readonly int $maxMovingTimeInSeconds;
2830

2931
private function __construct()
3032
{
@@ -41,6 +43,8 @@ private function __construct()
4143
$this->maxAverageSpeed = MetersPerSecond::from(8);
4244
$this->maxSpeed = MetersPerSecond::from(8);
4345
$this->averageHeartRate = null;
46+
$this->minMovingTimeInSeconds = 100;
47+
$this->maxMovingTimeInSeconds = 200;
4448
}
4549

4650
public static function fromDefaults(): self
@@ -64,6 +68,8 @@ public function build(): ActivityLap
6468
maxSpeed: $this->maxSpeed,
6569
elevationDifference: $this->elevationDifference,
6670
averageHeartRate: $this->averageHeartRate,
71+
minMovingTimeInSeconds: $this->minMovingTimeInSeconds,
72+
maxMovingTimeInSeconds: $this->maxMovingTimeInSeconds,
6773
);
6874
}
6975

0 commit comments

Comments
 (0)