Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

[556] feat: support stacked classes #702

Open
wants to merge 6 commits into
base: v5-wip
Choose a base branch
from
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
8 changes: 8 additions & 0 deletions src/Api.Contract/SyncContracts.cs
Original file line number Diff line number Diff line change
Expand Up @@ -45,6 +45,14 @@ public SyncPostRequest()
/// - SinceDate
/// </summary>
public ICollection<string> WorkoutIds { get; init; }

/// <summary>
/// True if these workouts should be combined (stacked) into a single final workout.
/// When False, the sync will still honor any settings configured in StackedClassesSettings.
/// When True, the sync will ignore the StackedClassesSettings and attempt stack all classes of the same
/// type, regardless of the time gap between them.
/// </summary>
public bool ForceStackWorkouts { get; init; } = false;
}

public record SyncPostResponse
Expand Down
2 changes: 1 addition & 1 deletion src/Api/Controllers/SyncController.cs
Original file line number Diff line number Diff line change
Expand Up @@ -52,7 +52,7 @@ public async Task<ActionResult<SyncPostResponse>> SyncAsync([FromBody] SyncPostR
SyncResult syncResult = new();
try
{
syncResult = await _syncService.SyncAsync(request.WorkoutIds, exclude: null);
syncResult = await _syncService.SyncAsync(request.WorkoutIds, exclude: null, forceStackWorkouts: request.ForceStackWorkouts);
}
catch (Exception e)
{
Expand Down
2 changes: 1 addition & 1 deletion src/ClientUI/ServiceClient.cs
Original file line number Diff line number Diff line change
Expand Up @@ -303,7 +303,7 @@ public async Task<SyncPostResponse> SyncPostAsync(SyncPostRequest syncPostReques
SyncResult syncResult = new();
try
{
syncResult = await _syncService.SyncAsync(syncPostRequest.WorkoutIds, exclude: null);
syncResult = await _syncService.SyncAsync(syncPostRequest.WorkoutIds, exclude: null, forceStackWorkouts: syncPostRequest.ForceStackWorkouts);
}
catch (Exception e)
{
Expand Down
3 changes: 1 addition & 2 deletions src/Common/Dto/P2GWorkout.cs
Original file line number Diff line number Diff line change
Expand Up @@ -6,14 +6,13 @@ namespace Common.Dto
public class P2GWorkout
{
public WorkoutType WorkoutType => GetWorkoutType();
public bool IsStackedWorkout { get; set; }

public UserData UserData { get; set; }
public Workout Workout { get; set; }
public WorkoutSamples WorkoutSamples { get; set; }
public ICollection<P2GExercise> Exercises { get; set; }

public dynamic Raw { get; set; }

private WorkoutType GetWorkoutType()
{
if (Workout is null) return WorkoutType.None;
Expand Down
20 changes: 19 additions & 1 deletion src/Common/Dto/Settings.cs
Original file line number Diff line number Diff line change
Expand Up @@ -78,7 +78,25 @@ public Format()
public Cycling Cycling { get; set; }
public Running Running { get; set; }
public Rowing Rowing { get; init; }
public Strength Strength { get; init; }
public Strength Strength { get; init; }
public StackedWorkoutsSettings StackedWorkouts { get; init; } = new StackedWorkoutsSettings();
}

public record StackedWorkoutsSettings
{
/// <summary>
/// True if P2G should automatically detect and stack workouts when
/// converting and syncing. P2G will only stack workouts of the same type
/// and only within a default time gap of 5min.
/// </summary>
public bool AutomaticallyStackWorkouts { get; set; } = false;

/// <summary>
/// The maximum amount of time allowed between workouts that should be stacked.
/// If the gap of time is larger than this, then the workouts will not be stacked.
/// The default is 5min.
/// </summary>
public int MaxAllowedGapSeconds { get; set; } = 300;
}

public record Cycling
Expand Down
39 changes: 31 additions & 8 deletions src/SharedUI/Pages/Sync.razor
Original file line number Diff line number Diff line change
Expand Up @@ -15,9 +15,33 @@
</div>
}

<HxButton OnClick="SyncAsync" Color="ThemeColor.Primary" Enabled="@IsSyncButtonEnabled()">
Sync <span class="badge bg-secondary">@selectedItems.Count()</span>
</HxButton>
<HxCard>
<BodyTemplate>
<div class="row">
<div class="col-md-4">
<HxSwitch @bind-Value="forceStackWorkouts" Inline="true">
<TextTemplate>
Stack Workouts
<HxPopover Trigger="PopoverTrigger.Hover|PopoverTrigger.Click|PopoverTrigger.Focus"
Title="<b>Stack Workouts</b>"
Content="@StackWorkoutsDocumentation"
Html="true">
<HxIcon Icon="@BootstrapIcon.QuestionCircle" />
</HxPopover>
</TextTemplate>
</HxSwitch>

</div>
<div class="col-md-4">
</div>
<div class="col-md-4 text-end">
<HxButton OnClick="SyncAsync" Color="ThemeColor.Primary" Enabled="@IsSyncButtonEnabled()">
Sync <span class="badge bg-secondary">@selectedItems.Count()</span>
</HxButton>
</div>
</div>
</BodyTemplate>
</HxCard>

<HxGrid TItem="PelotonWorkout" MultiSelectionEnabled="true" Responsive="true" DataProvider="LoadDataAsync" @bind-SelectedDataItems="selectedItems" PageSize="PageSize" SelectionEnabled="false">
<Columns>
Expand All @@ -29,15 +53,12 @@
</Columns>
</HxGrid>

<HxButton OnClick="SyncAsync" Color="ThemeColor.Primary" Enabled="@IsSyncButtonEnabled()">
Sync <span class="badge bg-secondary">@selectedItems.Count()</span>
</HxButton>

@code {

private HashSet<PelotonWorkout> selectedItems = new();
private static int PageSize = 25;
private bool PelotonSettingNotConfiguredYet = false;
private bool forceStackWorkouts = false;

private GarminMfaModal? _garminMfaModal;

Expand Down Expand Up @@ -101,7 +122,7 @@

try
{
var result = await _apiClient.SyncPostAsync(new SyncPostRequest() { WorkoutIds = selectedItems.Select(i => i.Id!).ToList() });
var result = await _apiClient.SyncPostAsync(new SyncPostRequest() { WorkoutIds = selectedItems.Select(i => i.Id!).ToList(), ForceStackWorkouts = forceStackWorkouts });
selectedItems.Clear();

if (result.SyncSuccess)
Expand Down Expand Up @@ -142,4 +163,6 @@
{
return selectedItems.Any();
}

private string StackWorkoutsDocumentation => $"Checking this will force the selected workouts to be combined into a single workout. Selecting this will override anything you have configured in Settings. It will stack the workouts regardless of how much time is between them so long as the workouts are of the same type..";
}
2 changes: 1 addition & 1 deletion src/SharedUI/Shared/AppSettingsForm.razor
Original file line number Diff line number Diff line change
Expand Up @@ -13,7 +13,7 @@
Title="<b>Automatic Syncing</b>"
Content="@AutomaticSyncingDocumentation"
Html="true">
<HxBadge Type="BadgeType.RoundedPill" Color="ThemeColor.Info">?</HxBadge>
<HxIcon Icon="@BootstrapIcon.QuestionCircle" />
</HxPopover>
</HeaderTemplate>
<BodyTemplate>
Expand Down
34 changes: 28 additions & 6 deletions src/SharedUI/Shared/FormatSettingsForm.razor
Original file line number Diff line number Diff line change
Expand Up @@ -16,7 +16,7 @@
Title="<b>Convert to various Format Types</b>"
Content="@FormatSettingsDocumentation"
Html="true">
<HxBadge Type="BadgeType.RoundedPill" Color="ThemeColor.Info">?</HxBadge>
<HxIcon Icon="@BootstrapIcon.QuestionCircle" />
</HxPopover>
</HeaderTemplate>
<BodyTemplate>
Expand All @@ -38,7 +38,7 @@
Title="<b>Workout Title Templating</b>"
Content="@WorkoutTitleDocumentation"
Html="true">
<HxBadge Type="BadgeType.RoundedPill" Color="ThemeColor.Info">?</HxBadge>
<HxIcon Icon="@BootstrapIcon.QuestionCircle" />
</HxPopover>
</HeaderTemplate>
<BodyTemplate>
Expand All @@ -57,7 +57,7 @@
Title="<b>Customzing Lap Types</b>"
Content="@LapTypesDocumentation"
Html="true">
<HxBadge Type="BadgeType.RoundedPill" Color="ThemeColor.Info">?</HxBadge>
<HxIcon Icon="@BootstrapIcon.QuestionCircle" />
</HxPopover>
</HeaderTemplate>
<BodyTemplate>
Expand Down Expand Up @@ -98,7 +98,7 @@
Title="<b>Strength Workouts</b>"
Content="@StrengthDocumentation"
Html="true">
<HxBadge Type="BadgeType.RoundedPill" Color="ThemeColor.Info">?</HxBadge>
<HxIcon Icon="@BootstrapIcon.QuestionCircle" />
</HxPopover>
</HeaderTemplate>
<BodyTemplate>
Expand All @@ -108,6 +108,27 @@
</div>
</div>
<br />
<div class="row g-3">
<div class="col-md-12">
<HxCard>
<HeaderTemplate>
Stacked Workouts
<HxPopover Trigger="PopoverTrigger.Hover|PopoverTrigger.Click|PopoverTrigger.Focus"
Title="<b>Stacked Workouts</b>"
Content="@StackedWorkoutsDocumentation"
Html="true">
<HxIcon Icon="@BootstrapIcon.QuestionCircle" />
</HxPopover>
</HeaderTemplate>
<BodyTemplate>
<HxSwitch Text="Enable Automatic Workout Stacking" @bind-Value="formatSettings.StackedWorkouts.AutomaticallyStackWorkouts" />
<br />
<HxInputNumber Label="Max Seconds between Stacked Workouts" TValue="int" @bind-Value="formatSettings.StackedWorkouts.MaxAllowedGapSeconds" Hint="A value of 300 would mean any workout started within 300s (5min) of the previous one would be stacked." />
</BodyTemplate>
</HxCard>
</div>
</div>
<br />
<div class="row gy-5">
<div class="md-col-12">

Expand All @@ -129,7 +150,7 @@
Title="<b>Device Info Settings</b>"
Content="@CustomDeviceInfoDocumentation"
Html="true">
<HxBadge Type="BadgeType.RoundedPill" Color="ThemeColor.Info">?</HxBadge>
<HxIcon Icon="@BootstrapIcon.QuestionCircle" />
</HxPopover>
</HeaderTemplate>
<BodyTemplate>
Expand Down Expand Up @@ -183,7 +204,7 @@
Title="<b>Custom Zone Handling</b>"
Content="@CustomZoneHandlingDocumentation"
Html="true">
<HxBadge Type="BadgeType.RoundedPill" Color="ThemeColor.Info">?</HxBadge>
<HxIcon Icon="@BootstrapIcon.QuestionCircle" />
</HxPopover>
</HeaderTemplate>
<BodyTemplate>
Expand Down Expand Up @@ -221,7 +242,7 @@
private string configDocumentationBase;
private string outputDirectory;

public FormatSettingsForm()

Check warning on line 245 in src/SharedUI/Shared/FormatSettingsForm.razor

View workflow job for this annotation

GitHub Actions / build-and-test (9.0.101, windows-latest, net9.0-windows10.0.19041.0)

Non-nullable field '_gridComponent' must contain a non-null value when exiting constructor. Consider adding the 'required' modifier or declaring the field as nullable.

Check warning on line 245 in src/SharedUI/Shared/FormatSettingsForm.razor

View workflow job for this annotation

GitHub Actions / Publish UI Distribution (9.0.101, net9.0-windows10.0.19041.0, win-x64)

Non-nullable field '_gridComponent' must contain a non-null value when exiting constructor. Consider adding the 'required' modifier or declaring the field as nullable.
{
var settings = new SettingsGetResponse();
formatSettings = settings.Format;
Expand Down Expand Up @@ -360,6 +381,7 @@
private string WorkoutTitleDocumentation => $"Allows you to customize how your workout title will appear in Garmin Connect using <a href='https://github.com/Handlebars-Net/Handlebars.Net'>Handlebars templates</a>. Some characters are not allowed, these characters will automatically be replaced with `-`. Below are the data fields you can use in the template:<br /><br /> <ul><li>PelotonWorkoutTitle</li><li>PelotonInstructorName</li></ul><br /><a href='{configDocumentation}#workout-title-templating'>Documentation</a><br /><small>(click the <b>?</b> to pin this window)</small>";
private string LapTypesDocumentation => $"Lap type defines how/when P2G will create a new Lap within a workout. This can be customized per Cardio type. To read more about each Lap Type please see the documentation.<br /><br /><a href='{configDocumentation}'>Documentation</a><br /><small>(click the <b>?</b> to pin this window)</small>";
private string StrengthDocumentation => $"Some Strength workouts have you do an exercise for time rather than for a specific number of reps. This setting allows you to customize how P2G should estimate the number of reps that were done in a time based exercise.<br /><br /><a href='{configDocumentation}'>Documentation</a><br /><small>(click the <b>?</b> to pin this window)</small>";
private string StackedWorkoutsDocumentation => $"A stacked workout is when you do several classes of the same type back to back and would like combined into a single final workout, instead of keeping them as individual workouts. In order for a workout to be stacked it must:<ol><li>Be of the same type as the other workouts in the stack</li><li>Have started within X seconds of the previous workouts end time</li></ol><br /><br /><a href='{configDocumentation}'>Documentation</a><br /><small>(click the <b>?</b> to pin this window)</small>";
private string CustomZoneHandlingDocumentation => $"If you are able to set your HR and Power Zones in Garmin Connect, then you do not need to enable these settings, Garmin will automatically calculate your time in zones correctly. If you are not able to configure your zones in Garmin Connect, then you can enable these settings to have P2G calculate time in zone information.<br /><br />If these are enabled, its likely Garmin will not calculate Training Load, Effect, or related data points.<br /><br /><a href='{configDocumentation}#understanding-p2g-provided-zones'>Documentation</a><br /><small>(click the <b>?</b> to pin this window)</small>";
private string CustomDeviceInfoDocumentation => $"The device used for a given exercise type can impact what additional data Garmin Connect will calculate (like TE, TSS, and VO2). P2G provides reasonable defaults, but you can customize what device you would like used here.<br /><br />The <strong>None</strong> Exercise Type serves as a global default. Meaning P2G will default to using this device if no more specific override is configured for a given Exercise type.<br /><br />If you delete all devices, P2G will restore its internal defaults.<br /><br /><a href='{configDocumentation}#customizing-the-garmin-device-associated-with-the-workout'>Documentation</a><br /><small>(click the <b>?</b> to pin this window)</small>";
private string SaveLocalCopyHint => $"Files will be saved to: {outputDirectory}.";
Expand Down
4 changes: 2 additions & 2 deletions src/SharedUI/Shared/GarminSettingsForm.razor
Original file line number Diff line number Diff line change
Expand Up @@ -63,7 +63,7 @@
Title="<b>Upload Format</b>"
Content="@UploadFormatDocumentation"
Html="true">
<HxBadge Type="BadgeType.RoundedPill" Color="ThemeColor.Info">?</HxBadge>
<HxIcon Icon="@BootstrapIcon.QuestionCircle" />
</HxPopover>
</HeaderTemplate>
<BodyTemplate>
Expand All @@ -75,7 +75,7 @@
</BodyTemplate>
</HxCard>
<br />
<HxCard>
<HxCard CssClass="border-danger text-danger">
<HeaderTemplate>
Garmin Api Settings
<HxPopover Trigger="PopoverTrigger.Hover|PopoverTrigger.Click|PopoverTrigger.Focus"
Expand Down
Loading
Loading