Skip to content

Commit b3ba60d

Browse files
atulmguptaCopilot
andcommitted
fix(apps): convert Driving Dynamics coach efficiency + distance to account units
The Driving Coach section hard-coded Wh/km efficiency labels/values and a per-drive "km" distance, so on an imperial account the per-drive table read e.g. "Wh/km" / "156" / "24 km" while the rest of the page showed mph/degF. The values were never converted because BuildCoach/BuildPerDrive did not receive the unit pref. - New tested converter UnitConverters.EfficiencyFromWhPerKm (Wh/km -> Wh/mi = x metres-per-mile/metres-per-km) + UnitLabels.EfficiencyLabel, with EfficiencyConverterTests (identity, 150 -> 241.4016 Wh/mi, labels). - Thread the UnitPref through BuildCoach + BuildPerDrive; convert avg/best efficiency, the per-drive efficiency value, the per-drive distance (km -> DistanceFromSi via metres), and make the efficiency column header unit-aware. Gates: full sln build 0 err, dotnet format clean, 31543 tests (+4) pass. Verified via live screenshot: per-drive table now shows "Wh/mi" / 251 / "1 mi" matching the imperial account. This establishes the shared converter the remaining hardcoded-label sites can adopt. Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
1 parent c2825d4 commit b3ba60d

4 files changed

Lines changed: 64 additions & 9 deletions

File tree

apps/windows/TeslaSync.App.Core/Units/UnitConverters.cs

Lines changed: 11 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -87,4 +87,15 @@ public static class UnitConverters
8787
PowerUnit.Kw => watts / 1000.0,
8888
_ => watts,
8989
};
90+
91+
/// <summary>
92+
/// Convert an energy-efficiency value from the backend-native Wh-per-kilometre (<c>efficiency_wh_km</c>)
93+
/// to the user's display distance unit. Efficiency per mile is larger than per km because a mile is
94+
/// longer: Wh/mi = Wh/km * (metres per mile / metres per km). Mirrors the web efficiency unit handling.
95+
/// </summary>
96+
public static double EfficiencyFromWhPerKm(double whPerKm, DistanceUnit to) => to switch
97+
{
98+
DistanceUnit.Mi => whPerKm * (MetersPerMile / MetersPerKm),
99+
_ => whPerKm,
100+
};
90101
}

apps/windows/TeslaSync.App.Core/Units/UnitPref.cs

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -128,6 +128,9 @@ public static class UnitLabels
128128
_ => "W",
129129
};
130130

131+
/// <summary>The energy-efficiency unit label for a display distance unit (Wh/mi imperial, else Wh/km).</summary>
132+
public static string EfficiencyLabel(DistanceUnit u) => u == DistanceUnit.Mi ? "Wh/mi" : "Wh/km";
133+
131134
public static DistanceUnit DistanceFromLabel(string label) => label switch
132135
{
133136
"km" => DistanceUnit.Km,
Lines changed: 41 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,41 @@
1+
using TeslaSync.App.Core.Units;
2+
using Xunit;
3+
4+
namespace TeslaSync.App.Tests;
5+
6+
/// <summary>
7+
/// Verifies the efficiency unit conversion + label that the unit-aware projections use to render the
8+
/// backend-native <c>efficiency_wh_km</c> in the account's display distance unit (web parity: Wh/km for
9+
/// metric, Wh/mi for imperial). Per-mile efficiency is larger than per-km because a mile is longer.
10+
/// </summary>
11+
public sealed class EfficiencyConverterTests
12+
{
13+
[Fact]
14+
public void WhPerKm_to_metric_is_identity()
15+
{
16+
Assert.Equal(150.0, UnitConverters.EfficiencyFromWhPerKm(150.0, DistanceUnit.Km), 6);
17+
}
18+
19+
[Fact]
20+
public void WhPerKm_to_imperial_scales_by_km_per_mile()
21+
{
22+
// 150 Wh/km * 1.609344 km/mi = 241.4016 Wh/mi
23+
Assert.Equal(241.4016, UnitConverters.EfficiencyFromWhPerKm(150.0, DistanceUnit.Mi), 4);
24+
}
25+
26+
[Fact]
27+
public void Zero_is_zero_in_both_units()
28+
{
29+
Assert.Equal(0.0, UnitConverters.EfficiencyFromWhPerKm(0.0, DistanceUnit.Mi), 6);
30+
Assert.Equal(0.0, UnitConverters.EfficiencyFromWhPerKm(0.0, DistanceUnit.Km), 6);
31+
}
32+
33+
[Theory]
34+
[InlineData(DistanceUnit.Km, "Wh/km")]
35+
[InlineData(DistanceUnit.Mi, "Wh/mi")]
36+
[InlineData(DistanceUnit.Ft, "Wh/km")]
37+
public void EfficiencyLabel_matches_distance_unit(DistanceUnit unit, string expected)
38+
{
39+
Assert.Equal(expected, UnitLabels.EfficiencyLabel(unit));
40+
}
41+
}

apps/windows/TeslaSync.App/feature-views/DrivingDynamicsPage/DrivingDynamicsPage.Projection.cs

Lines changed: 9 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -115,7 +115,7 @@ public static DrivingDynamicsDisplay Project(
115115
MotorCharts: BuildMotorCharts(snapshot.MotorHistory, localizer),
116116
Efficiency: BuildEfficiency(stats, units, localizer),
117117
Summary: BuildSummary(stats, units, localizer),
118-
Coach: BuildCoach(snapshot.Coach, localizer),
118+
Coach: BuildCoach(snapshot.Coach, units, localizer),
119119
Analytics: BuildAnalytics(filteredDrives, model.Range, units, localizer),
120120
Tips: BuildTips(stats, localizer),
121121
AutomationName: $"{title}. {subtitle}");
@@ -442,7 +442,7 @@ private static SummaryStatsDisplay BuildSummary(MotorStats? stats, UnitPref unit
442442
}
443443

444444
// ── 9. Driving Coach ─────────────────────────────────────────────────────────────────────────────────
445-
private static CoachDisplay BuildCoach(CoachData? coach, ILocalizer l)
445+
private static CoachDisplay BuildCoach(CoachData? coach, UnitPref units, ILocalizer l)
446446
{
447447
double score = coach?.OverallScore ?? 0;
448448
int total = coach?.TotalDrivesAnalyzed ?? 0;
@@ -451,7 +451,7 @@ private static CoachDisplay BuildCoach(CoachData? coach, ILocalizer l)
451451
var weekly = BuildWeeklySeries(coach, l);
452452
var patterns = BuildPatterns(coach, l);
453453
var (recsHas, recs) = BuildRecommendations(coach, l);
454-
var (perHas, perRows) = BuildPerDrive(coach);
454+
var (perHas, perRows) = BuildPerDrive(coach, units);
455455

456456
return new CoachDisplay(
457457
l.GetString("dynamics.coach.title", "Driving Coach"),
@@ -465,9 +465,9 @@ private static CoachDisplay BuildCoach(CoachData? coach, ILocalizer l)
465465
segments,
466466
rows,
467467
l.GetString("dynamics.coach.avgEfficiency", "Avg Efficiency"),
468-
$"{ScalarFormatters.FormatNumber(coach?.EfficiencyWhKm ?? 0, 0)} Wh/km",
468+
$"{ScalarFormatters.FormatNumber(UnitConverters.EfficiencyFromWhPerKm(coach?.EfficiencyWhKm ?? 0, units.Distance), 0)} {UnitLabels.EfficiencyLabel(units.Distance)}",
469469
l.GetString("dynamics.coach.bestEfficiency", "Best Efficiency"),
470-
$"{ScalarFormatters.FormatNumber(coach?.BestEfficiencyWhKm ?? 0, 0)} Wh/km",
470+
$"{ScalarFormatters.FormatNumber(UnitConverters.EfficiencyFromWhPerKm(coach?.BestEfficiencyWhKm ?? 0, units.Distance), 0)} {UnitLabels.EfficiencyLabel(units.Distance)}",
471471
l.GetString("dynamics.coach.weeklyTrend", "Weekly Score Trend"),
472472
(coach?.WeeklyTrend.Count ?? 0) > 1,
473473
l.GetString("dynamics.coach.needWeeks", "Need at least 2 weeks of data for trend analysis."),
@@ -485,7 +485,7 @@ private static CoachDisplay BuildCoach(CoachData? coach, ILocalizer l)
485485
l.GetString("dynamics.coach.col.date", "Date"),
486486
l.GetString("dynamics.coach.col.score", "Score"),
487487
l.GetString("dynamics.coach.col.style", "Style"),
488-
l.GetString("dynamics.coach.col.efficiency", "Wh/km"),
488+
UnitLabels.EfficiencyLabel(units.Distance),
489489
l.GetString("dynamics.coach.col.distance", "Distance"),
490490
],
491491
perRows);
@@ -578,7 +578,7 @@ private static (bool, IReadOnlyList<CoachRecRow>) BuildRecommendations(CoachData
578578
return (true, rows);
579579
}
580580

581-
private static (bool, IReadOnlyList<CoachScoreRow>) BuildPerDrive(CoachData? coach)
581+
private static (bool, IReadOnlyList<CoachScoreRow>) BuildPerDrive(CoachData? coach, UnitPref units)
582582
{
583583
var scores = coach?.PerDriveScores ?? System.Array.Empty<CoachDriveScore>();
584584
if (scores.Count == 0)
@@ -603,8 +603,8 @@ private static (bool, IReadOnlyList<CoachScoreRow>) BuildPerDrive(CoachData? coa
603603
scoreStatus,
604604
s.Style,
605605
styleStatus,
606-
ScalarFormatters.FormatNumber(s.Efficiency, 0),
607-
$"{ScalarFormatters.FormatNumber(s.Distance, 0)} km"));
606+
ScalarFormatters.FormatNumber(UnitConverters.EfficiencyFromWhPerKm(s.Efficiency, units.Distance), 0),
607+
$"{ScalarFormatters.FormatNumber(UnitConverters.DistanceFromSi(s.Distance * 1000.0, units.Distance), 0)} {UnitLabels.Label(units.Distance)}"));
608608
}
609609

610610
return (true, rows);

0 commit comments

Comments
 (0)