Retain drill follow-up learning on dashboard
This commit is contained in:
parent
ce4b46b156
commit
64c92dc978
2 changed files with 502 additions and 1 deletions
|
|
@ -687,6 +687,7 @@ void UHyperTwistCoachDashboardWidget::RefreshCoachDashboardView()
|
|||
SyncSelectedHandoffHistoryEntry();
|
||||
SyncSelectedQueueSuppressionHistoryEntry();
|
||||
SyncSelectedClosureRecoveryHistoryEntry();
|
||||
SyncSelectedMethodDrillFollowUpHistoryEntry();
|
||||
SyncPreferredGuidancePreviewLane(false);
|
||||
UpdateDashboardPresentation();
|
||||
}
|
||||
|
|
@ -1689,6 +1690,47 @@ bool UHyperTwistCoachDashboardWidget::SelectNextClosureRecoveryHistoryEntry()
|
|||
return true;
|
||||
}
|
||||
|
||||
bool UHyperTwistCoachDashboardWidget::SelectPreviousMethodDrillFollowUpHistoryEntry()
|
||||
{
|
||||
if (MethodDrillFollowUpHistoryEntries.Num() <= 0)
|
||||
{
|
||||
SelectedMethodDrillFollowUpHistoryIndex = INDEX_NONE;
|
||||
return false;
|
||||
}
|
||||
|
||||
if (SelectedMethodDrillFollowUpHistoryIndex == INDEX_NONE)
|
||||
{
|
||||
SyncSelectedMethodDrillFollowUpHistoryEntry();
|
||||
return SelectedMethodDrillFollowUpHistoryIndex != INDEX_NONE;
|
||||
}
|
||||
|
||||
SelectedMethodDrillFollowUpHistoryIndex =
|
||||
(SelectedMethodDrillFollowUpHistoryIndex - 1 + MethodDrillFollowUpHistoryEntries.Num())
|
||||
% MethodDrillFollowUpHistoryEntries.Num();
|
||||
UpdateDashboardPresentation();
|
||||
return true;
|
||||
}
|
||||
|
||||
bool UHyperTwistCoachDashboardWidget::SelectNextMethodDrillFollowUpHistoryEntry()
|
||||
{
|
||||
if (MethodDrillFollowUpHistoryEntries.Num() <= 0)
|
||||
{
|
||||
SelectedMethodDrillFollowUpHistoryIndex = INDEX_NONE;
|
||||
return false;
|
||||
}
|
||||
|
||||
if (SelectedMethodDrillFollowUpHistoryIndex == INDEX_NONE)
|
||||
{
|
||||
SyncSelectedMethodDrillFollowUpHistoryEntry();
|
||||
return SelectedMethodDrillFollowUpHistoryIndex != INDEX_NONE;
|
||||
}
|
||||
|
||||
SelectedMethodDrillFollowUpHistoryIndex =
|
||||
(SelectedMethodDrillFollowUpHistoryIndex + 1) % MethodDrillFollowUpHistoryEntries.Num();
|
||||
UpdateDashboardPresentation();
|
||||
return true;
|
||||
}
|
||||
|
||||
bool UHyperTwistCoachDashboardWidget::StartLiveAttemptTimer()
|
||||
{
|
||||
if (!CachedRunState.Session.IsStructurallyValid() || !CachedRunState.CurrentSelection.TrainingCase.IsStructurallyValid())
|
||||
|
|
@ -1954,7 +1996,12 @@ void UHyperTwistCoachDashboardWidget::HandleStartPreviewCaseClicked()
|
|||
|
||||
void UHyperTwistCoachDashboardWidget::HandleStartMethodDrillFollowUpClicked()
|
||||
{
|
||||
StartActiveMethodDrillFollowUpRun(DefaultSessionId + TEXT("_method_drill_follow_up"));
|
||||
const FHyperTwistTrainingRunState RunState =
|
||||
StartActiveMethodDrillFollowUpRun(DefaultSessionId + TEXT("_method_drill_follow_up"));
|
||||
if (RunState.Session.IsStructurallyValid())
|
||||
{
|
||||
CaptureActiveMethodDrillFollowUpLaunchContext(RunState.Session.TrainingSessionId);
|
||||
}
|
||||
SyncRetainedRunRecap();
|
||||
SyncSelectedQueueEntry();
|
||||
SyncSelectedAttempt();
|
||||
|
|
@ -2020,6 +2067,16 @@ void UHyperTwistCoachDashboardWidget::HandleNextClosureRecoveryClicked()
|
|||
SelectNextClosureRecoveryHistoryEntry();
|
||||
}
|
||||
|
||||
void UHyperTwistCoachDashboardWidget::HandlePreviousMethodDrillFollowUpHistoryClicked()
|
||||
{
|
||||
SelectPreviousMethodDrillFollowUpHistoryEntry();
|
||||
}
|
||||
|
||||
void UHyperTwistCoachDashboardWidget::HandleNextMethodDrillFollowUpHistoryClicked()
|
||||
{
|
||||
SelectNextMethodDrillFollowUpHistoryEntry();
|
||||
}
|
||||
|
||||
void UHyperTwistCoachDashboardWidget::HandleClearRunClicked()
|
||||
{
|
||||
ClearActiveTrainingRun();
|
||||
|
|
@ -2463,6 +2520,57 @@ void UHyperTwistCoachDashboardWidget::EnsureDefaultDashboardBuilt()
|
|||
);
|
||||
StartMethodDrillFollowUpButtonLabel = StartMethodDrillFollowUpLabelRaw;
|
||||
}
|
||||
MethodDrillFollowUpHistoryHeaderTextBlock = HyperTwistCoachDashboardWidgetInternal::AddTextRow(
|
||||
WidgetTree,
|
||||
RootLayout,
|
||||
TEXT("CoachMethodDrillFollowUpHistoryHeader"),
|
||||
2
|
||||
);
|
||||
MethodDrillFollowUpHistoryStatusTextBlock = HyperTwistCoachDashboardWidgetInternal::AddTextRow(
|
||||
WidgetTree,
|
||||
RootLayout,
|
||||
TEXT("CoachMethodDrillFollowUpHistoryStatus"),
|
||||
2
|
||||
);
|
||||
MethodDrillFollowUpHistoryDetailTextBlock = HyperTwistCoachDashboardWidgetInternal::AddTextRow(
|
||||
WidgetTree,
|
||||
RootLayout,
|
||||
TEXT("CoachMethodDrillFollowUpHistoryDetail"),
|
||||
8
|
||||
);
|
||||
UHorizontalBox* MethodDrillHistoryNavRow = WidgetTree->ConstructWidget<UHorizontalBox>(
|
||||
UHorizontalBox::StaticClass(),
|
||||
TEXT("CoachDashboardMethodDrillHistoryNavRow")
|
||||
);
|
||||
if (MethodDrillHistoryNavRow != nullptr)
|
||||
{
|
||||
if (UVerticalBoxSlot* MethodDrillHistoryNavRowSlot = RootLayout->AddChildToVerticalBox(MethodDrillHistoryNavRow))
|
||||
{
|
||||
MethodDrillHistoryNavRowSlot->SetPadding(FMargin(0.0f, 4.0f, 0.0f, 0.0f));
|
||||
}
|
||||
|
||||
UTextBlock* PreviousMethodDrillHistoryLabelRaw = nullptr;
|
||||
PreviousMethodDrillFollowUpHistoryButton = HyperTwistCoachDashboardWidgetInternal::AddButton(
|
||||
WidgetTree,
|
||||
MethodDrillHistoryNavRow,
|
||||
TEXT("PreviousMethodDrillFollowUpHistoryButton"),
|
||||
TEXT("PreviousMethodDrillFollowUpHistoryButtonLabel"),
|
||||
TEXT("Prev Drill Follow-Up"),
|
||||
PreviousMethodDrillHistoryLabelRaw
|
||||
);
|
||||
PreviousMethodDrillFollowUpHistoryButtonLabel = PreviousMethodDrillHistoryLabelRaw;
|
||||
|
||||
UTextBlock* NextMethodDrillHistoryLabelRaw = nullptr;
|
||||
NextMethodDrillFollowUpHistoryButton = HyperTwistCoachDashboardWidgetInternal::AddButton(
|
||||
WidgetTree,
|
||||
MethodDrillHistoryNavRow,
|
||||
TEXT("NextMethodDrillFollowUpHistoryButton"),
|
||||
TEXT("NextMethodDrillFollowUpHistoryButtonLabel"),
|
||||
TEXT("Next Drill Follow-Up"),
|
||||
NextMethodDrillHistoryLabelRaw
|
||||
);
|
||||
NextMethodDrillFollowUpHistoryButtonLabel = NextMethodDrillHistoryLabelRaw;
|
||||
}
|
||||
UHorizontalBox* RunRecapActionRow = WidgetTree->ConstructWidget<UHorizontalBox>(
|
||||
UHorizontalBox::StaticClass(),
|
||||
TEXT("CoachDashboardRunRecapActions")
|
||||
|
|
@ -3464,6 +3572,20 @@ void UHyperTwistCoachDashboardWidget::EnsureDefaultDashboardBuilt()
|
|||
&UHyperTwistCoachDashboardWidget::HandleStartMethodDrillFollowUpClicked
|
||||
);
|
||||
}
|
||||
if (PreviousMethodDrillFollowUpHistoryButton != nullptr)
|
||||
{
|
||||
PreviousMethodDrillFollowUpHistoryButton->OnClicked.AddDynamic(
|
||||
this,
|
||||
&UHyperTwistCoachDashboardWidget::HandlePreviousMethodDrillFollowUpHistoryClicked
|
||||
);
|
||||
}
|
||||
if (NextMethodDrillFollowUpHistoryButton != nullptr)
|
||||
{
|
||||
NextMethodDrillFollowUpHistoryButton->OnClicked.AddDynamic(
|
||||
this,
|
||||
&UHyperTwistCoachDashboardWidget::HandleNextMethodDrillFollowUpHistoryClicked
|
||||
);
|
||||
}
|
||||
if (CompleteRunButton != nullptr)
|
||||
{
|
||||
CompleteRunButton->OnClicked.AddDynamic(this, &UHyperTwistCoachDashboardWidget::HandleCompleteRunClicked);
|
||||
|
|
@ -3641,6 +3763,16 @@ void UHyperTwistCoachDashboardWidget::SyncRetainedRunRecap()
|
|||
RetainedGuidanceComparisonDeck = ActiveGuidanceLaunchDeck;
|
||||
RetainedGuidanceComparisonSelectedCaseId = ActiveGuidanceLaunchSelectedCaseId;
|
||||
}
|
||||
if (!ActiveMethodDrillFollowUpLaunchSessionId.IsEmpty()
|
||||
&& ActiveMethodDrillFollowUpLaunchSessionId == RetainedRunRecapSessionId
|
||||
&& ActiveMethodDrillFollowUpLaunchDiagnosticPacket.IsStructurallyValid())
|
||||
{
|
||||
RecordMethodDrillFollowUpHistoryEntry(RetainedRunRecapSummary);
|
||||
ActiveMethodDrillFollowUpLaunchSessionId.Reset();
|
||||
ActiveMethodDrillFollowUpLaunchDiagnosticPacket =
|
||||
FHyperTwistTrainingMethodDrillDiagnosticPacket();
|
||||
ActiveMethodDrillFollowUpLaunchTemplate = FHyperTwistTrainingSessionTemplate();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
|
@ -3720,6 +3852,21 @@ void UHyperTwistCoachDashboardWidget::SyncSelectedClosureRecoveryHistoryEntry()
|
|||
}
|
||||
}
|
||||
|
||||
void UHyperTwistCoachDashboardWidget::SyncSelectedMethodDrillFollowUpHistoryEntry()
|
||||
{
|
||||
if (MethodDrillFollowUpHistoryEntries.Num() <= 0)
|
||||
{
|
||||
SelectedMethodDrillFollowUpHistoryIndex = INDEX_NONE;
|
||||
return;
|
||||
}
|
||||
|
||||
if (SelectedMethodDrillFollowUpHistoryIndex < 0
|
||||
|| SelectedMethodDrillFollowUpHistoryIndex >= MethodDrillFollowUpHistoryEntries.Num())
|
||||
{
|
||||
SelectedMethodDrillFollowUpHistoryIndex = MethodDrillFollowUpHistoryEntries.Num() - 1;
|
||||
}
|
||||
}
|
||||
|
||||
void UHyperTwistCoachDashboardWidget::SyncPreferredGuidancePreviewLane(const bool bForceAdoptPreferredLane)
|
||||
{
|
||||
const FHyperTwistCoachDashboardGuidanceLanePreference Preference = BuildGuidanceLanePreference();
|
||||
|
|
@ -3793,6 +3940,15 @@ void UHyperTwistCoachDashboardWidget::CaptureActiveGuidanceLaunchContext(
|
|||
}
|
||||
}
|
||||
|
||||
void UHyperTwistCoachDashboardWidget::CaptureActiveMethodDrillFollowUpLaunchContext(
|
||||
const FString& SessionId
|
||||
)
|
||||
{
|
||||
ActiveMethodDrillFollowUpLaunchSessionId = SessionId;
|
||||
ActiveMethodDrillFollowUpLaunchDiagnosticPacket = CachedMethodDrillDiagnosticPacket;
|
||||
ActiveMethodDrillFollowUpLaunchTemplate = CachedMethodDrillFollowUpTemplate;
|
||||
}
|
||||
|
||||
FHyperTwistTrainingDeck UHyperTwistCoachDashboardWidget::BuildSelectedGuidancePreviewCaseDeck() const
|
||||
{
|
||||
const FHyperTwistTrainingDeck* Deck = GetActiveGuidancePreviewDeck();
|
||||
|
|
@ -4522,6 +4678,74 @@ FHyperTwistCoachDashboardQueueRecoveryWeighting UHyperTwistCoachDashboardWidget:
|
|||
return Weighting;
|
||||
}
|
||||
|
||||
FHyperTwistCoachDashboardMethodDrillFollowUpWeighting
|
||||
UHyperTwistCoachDashboardWidget::BuildMethodDrillFollowUpWeighting() const
|
||||
{
|
||||
FHyperTwistCoachDashboardMethodDrillFollowUpWeighting Weighting;
|
||||
if (!CachedMethodDrillDiagnosticPacket.IsStructurallyValid())
|
||||
{
|
||||
return Weighting;
|
||||
}
|
||||
|
||||
constexpr int32 MaxMatchedHistoryEntries = 6;
|
||||
for (int32 Index = MethodDrillFollowUpHistoryEntries.Num() - 1;
|
||||
Index >= 0 && Weighting.MatchingHistoryCount < MaxMatchedHistoryEntries;
|
||||
--Index)
|
||||
{
|
||||
const FHyperTwistCoachDashboardMethodDrillFollowUpHistoryEntry& Entry =
|
||||
MethodDrillFollowUpHistoryEntries[Index];
|
||||
if (Entry.DiagnosticState != CachedMethodDrillDiagnosticPacket.DiagnosticState
|
||||
|| Entry.RecommendedReviewGrade
|
||||
!= CachedMethodDrillDiagnosticPacket.RecommendedReviewGrade)
|
||||
{
|
||||
continue;
|
||||
}
|
||||
|
||||
++Weighting.MatchingHistoryCount;
|
||||
Weighting.AverageSuccessRate += Entry.SuccessRate;
|
||||
Weighting.AverageMistakes += static_cast<float>(Entry.TotalMistakes);
|
||||
|
||||
const bool bStrongOutcome =
|
||||
Entry.SuccessRate >= 0.72f
|
||||
&& Entry.TotalMistakes <= FMath::Max(1, Entry.CompletedAttemptCount);
|
||||
const bool bWeakOutcome =
|
||||
Entry.SuccessRate < 0.50f
|
||||
|| Entry.TotalMistakes > FMath::Max(2, Entry.CompletedAttemptCount * 2);
|
||||
|
||||
if (bStrongOutcome)
|
||||
{
|
||||
++Weighting.StrongOutcomeCount;
|
||||
Weighting.ContinueNarrowBias += 2;
|
||||
}
|
||||
if (Entry.bBoundaryPending || Entry.bBoundaryRepeatBurden)
|
||||
{
|
||||
++Weighting.ContinueNarrowBias;
|
||||
}
|
||||
if (bWeakOutcome)
|
||||
{
|
||||
++Weighting.WeakOutcomeCount;
|
||||
Weighting.RedirectCoachBias += 2;
|
||||
}
|
||||
if (!Entry.bBoundaryPending && Entry.SuccessRate < 0.65f)
|
||||
{
|
||||
++Weighting.RedirectCoachBias;
|
||||
}
|
||||
}
|
||||
|
||||
if (Weighting.MatchingHistoryCount <= 0)
|
||||
{
|
||||
return Weighting;
|
||||
}
|
||||
|
||||
Weighting.AverageSuccessRate /= static_cast<float>(Weighting.MatchingHistoryCount);
|
||||
Weighting.AverageMistakes /= static_cast<float>(Weighting.MatchingHistoryCount);
|
||||
Weighting.bPreferDrillFollowUp =
|
||||
Weighting.ContinueNarrowBias >= Weighting.RedirectCoachBias + 2;
|
||||
Weighting.bPreferCoachHandoff =
|
||||
Weighting.RedirectCoachBias >= Weighting.ContinueNarrowBias + 2;
|
||||
return Weighting;
|
||||
}
|
||||
|
||||
FString UHyperTwistCoachDashboardWidget::BuildGuidanceOutcomeActionLabel(
|
||||
const EHyperTwistCoachDashboardOutcomeAction Action) const
|
||||
{
|
||||
|
|
@ -4941,6 +5165,71 @@ void UHyperTwistCoachDashboardWidget::RecordClosureRecoverySnapshot()
|
|||
SelectedClosureRecoveryIndex = ClosureRecoveryHistoryEntries.Num() - 1;
|
||||
}
|
||||
|
||||
void UHyperTwistCoachDashboardWidget::RecordMethodDrillFollowUpHistoryEntry(
|
||||
const FHyperTwistTrainingSessionSummary& CompletedRunRecap
|
||||
)
|
||||
{
|
||||
if (CompletedRunRecap.TrainingSessionId.IsEmpty()
|
||||
|| !ActiveMethodDrillFollowUpLaunchDiagnosticPacket.IsStructurallyValid()
|
||||
|| !ActiveMethodDrillFollowUpLaunchTemplate.IsStructurallyValid())
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
FHyperTwistCoachDashboardMethodDrillFollowUpHistoryEntry Entry;
|
||||
Entry.RecordedAtUtc = FDateTime::UtcNow().ToIso8601();
|
||||
Entry.SourceRunId = ActiveMethodDrillFollowUpLaunchDiagnosticPacket.RunId;
|
||||
Entry.SourceDrillId = ActiveMethodDrillFollowUpLaunchDiagnosticPacket.DrillId;
|
||||
Entry.SourceSessionId = CompletedRunRecap.TrainingSessionId;
|
||||
Entry.StageLabel = ActiveMethodDrillFollowUpLaunchDiagnosticPacket.StageLabel;
|
||||
Entry.DiagnosticState = ActiveMethodDrillFollowUpLaunchDiagnosticPacket.DiagnosticState;
|
||||
Entry.RecommendedReviewGrade =
|
||||
ActiveMethodDrillFollowUpLaunchDiagnosticPacket.RecommendedReviewGrade;
|
||||
Entry.TemplateKind = ActiveMethodDrillFollowUpLaunchTemplate.TemplateKind;
|
||||
Entry.SuggestedMode = ActiveMethodDrillFollowUpLaunchTemplate.SuggestedMode;
|
||||
Entry.SuggestedSelectionPolicy =
|
||||
ActiveMethodDrillFollowUpLaunchTemplate.SuggestedSelectionPolicy;
|
||||
Entry.FocusCaseCount = ActiveMethodDrillFollowUpLaunchDiagnosticPacket.FocusCaseIds.Num();
|
||||
Entry.LaunchedCaseCount = CompletedRunRecap.CompletedAttemptCount > 0
|
||||
? FMath::Max(CompletedRunRecap.CompletedAttemptCount, 1)
|
||||
: ActiveMethodDrillFollowUpLaunchTemplate.RecommendedCaseCount;
|
||||
Entry.CompletedAttemptCount = CompletedRunRecap.CompletedAttemptCount;
|
||||
Entry.TotalMistakes = CompletedRunRecap.TotalMistakes;
|
||||
Entry.SuccessRate = CompletedRunRecap.SuccessRate;
|
||||
Entry.bBoundaryPending =
|
||||
ActiveMethodDrillFollowUpLaunchDiagnosticPacket.bBoundaryTriggeredByPendingCases;
|
||||
Entry.bBoundaryRepeatBurden =
|
||||
ActiveMethodDrillFollowUpLaunchDiagnosticPacket.bBoundaryTriggeredByRepeatBurden;
|
||||
Entry.bBoundaryAssistance =
|
||||
ActiveMethodDrillFollowUpLaunchDiagnosticPacket.bBoundaryTriggeredByAssistance;
|
||||
if (!Entry.IsStructurallyValid())
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
const bool bAlreadyRecorded = MethodDrillFollowUpHistoryEntries.ContainsByPredicate(
|
||||
[&Entry](const FHyperTwistCoachDashboardMethodDrillFollowUpHistoryEntry& Candidate)
|
||||
{
|
||||
return Candidate.SourceRunId == Entry.SourceRunId
|
||||
&& Candidate.SourceSessionId == Entry.SourceSessionId;
|
||||
}
|
||||
);
|
||||
if (bAlreadyRecorded)
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
MethodDrillFollowUpHistoryEntries.Add(Entry);
|
||||
constexpr int32 MaxRetainedMethodDrillFollowUpHistoryEntries = 16;
|
||||
if (MethodDrillFollowUpHistoryEntries.Num() > MaxRetainedMethodDrillFollowUpHistoryEntries)
|
||||
{
|
||||
const int32 ExcessCount =
|
||||
MethodDrillFollowUpHistoryEntries.Num() - MaxRetainedMethodDrillFollowUpHistoryEntries;
|
||||
MethodDrillFollowUpHistoryEntries.RemoveAt(0, ExcessCount, EAllowShrinking::No);
|
||||
}
|
||||
SelectedMethodDrillFollowUpHistoryIndex = MethodDrillFollowUpHistoryEntries.Num() - 1;
|
||||
}
|
||||
|
||||
void UHyperTwistCoachDashboardWidget::UpdateDashboardPresentation()
|
||||
{
|
||||
const bool bHasActiveRun = CachedRunState.Session.IsStructurallyValid()
|
||||
|
|
@ -5294,6 +5583,80 @@ void UHyperTwistCoachDashboardWidget::UpdateDashboardPresentation()
|
|||
{
|
||||
StartMethodDrillFollowUpLabel = TEXT("No Drill Follow-Up");
|
||||
}
|
||||
const FHyperTwistCoachDashboardMethodDrillFollowUpWeighting MethodDrillFollowUpWeighting =
|
||||
BuildMethodDrillFollowUpWeighting();
|
||||
const FHyperTwistCoachDashboardMethodDrillFollowUpHistoryEntry* SelectedMethodDrillFollowUpHistoryEntry =
|
||||
SelectedMethodDrillFollowUpHistoryIndex >= 0
|
||||
&& SelectedMethodDrillFollowUpHistoryIndex < MethodDrillFollowUpHistoryEntries.Num()
|
||||
? &MethodDrillFollowUpHistoryEntries[SelectedMethodDrillFollowUpHistoryIndex]
|
||||
: nullptr;
|
||||
const FString MethodDrillOutcomePreferenceLine = !MethodDrillFollowUpWeighting.IsStructurallyValid()
|
||||
? TEXT("no retained drill follow-up history yet")
|
||||
: (MethodDrillFollowUpWeighting.bPreferDrillFollowUp
|
||||
? TEXT("retained drill history favors staying narrow with drill follow-up")
|
||||
: (MethodDrillFollowUpWeighting.bPreferCoachHandoff
|
||||
? TEXT("retained drill history favors handing control back to broader coach guidance")
|
||||
: TEXT("retained drill history is balanced")));
|
||||
const FString MethodDrillFollowUpHistoryStatusLine =
|
||||
SelectedMethodDrillFollowUpHistoryEntry != nullptr
|
||||
? FString::Printf(
|
||||
TEXT("Drill follow-up history: %d entries | selected %d/%d | Stage: %s | review %s | Recorded: %s"),
|
||||
MethodDrillFollowUpHistoryEntries.Num(),
|
||||
SelectedMethodDrillFollowUpHistoryIndex + 1,
|
||||
MethodDrillFollowUpHistoryEntries.Num(),
|
||||
SelectedMethodDrillFollowUpHistoryEntry->StageLabel.IsEmpty()
|
||||
? TEXT("n/a")
|
||||
: *SelectedMethodDrillFollowUpHistoryEntry->StageLabel,
|
||||
*HyperTwistCoachDashboardWidgetInternal::EnumDisplayName(
|
||||
SelectedMethodDrillFollowUpHistoryEntry->RecommendedReviewGrade),
|
||||
*SelectedMethodDrillFollowUpHistoryEntry->RecordedAtUtc)
|
||||
: TEXT("Drill follow-up history: no retained drill follow-up outcomes recorded yet.");
|
||||
const FString MethodDrillFollowUpHistoryDetailLine =
|
||||
SelectedMethodDrillFollowUpHistoryEntry != nullptr
|
||||
? FString::Printf(
|
||||
TEXT("State: %s | template: %s | mode: %s | selection: %s | focus %d | launched %d | success %.2f | attempts %d | mistakes %d | pending: %s | repeat burden: %s | assistance: %s"),
|
||||
*HyperTwistCoachDashboardWidgetInternal::EnumDisplayName(
|
||||
SelectedMethodDrillFollowUpHistoryEntry->DiagnosticState),
|
||||
*HyperTwistCoachDashboardWidgetInternal::EnumDisplayName(
|
||||
SelectedMethodDrillFollowUpHistoryEntry->TemplateKind),
|
||||
*HyperTwistCoachDashboardWidgetInternal::EnumDisplayName(
|
||||
SelectedMethodDrillFollowUpHistoryEntry->SuggestedMode),
|
||||
*HyperTwistCoachDashboardWidgetInternal::EnumDisplayName(
|
||||
SelectedMethodDrillFollowUpHistoryEntry->SuggestedSelectionPolicy),
|
||||
SelectedMethodDrillFollowUpHistoryEntry->FocusCaseCount,
|
||||
SelectedMethodDrillFollowUpHistoryEntry->LaunchedCaseCount,
|
||||
SelectedMethodDrillFollowUpHistoryEntry->SuccessRate,
|
||||
SelectedMethodDrillFollowUpHistoryEntry->CompletedAttemptCount,
|
||||
SelectedMethodDrillFollowUpHistoryEntry->TotalMistakes,
|
||||
SelectedMethodDrillFollowUpHistoryEntry->bBoundaryPending ? TEXT("yes") : TEXT("no"),
|
||||
SelectedMethodDrillFollowUpHistoryEntry->bBoundaryRepeatBurden ? TEXT("yes") : TEXT("no"),
|
||||
SelectedMethodDrillFollowUpHistoryEntry->bBoundaryAssistance ? TEXT("yes") : TEXT("no"))
|
||||
: TEXT("Drill follow-up history detail: once drill-derived follow-up runs complete, their recap outcomes will accumulate here and feed later drill-vs-handoff judgment.");
|
||||
if (MethodDrillFollowUpWeighting.IsStructurallyValid())
|
||||
{
|
||||
MethodDrillDiagnosticsStatusLine = FString::Printf(
|
||||
TEXT("%s | history: %s"),
|
||||
*MethodDrillDiagnosticsStatusLine,
|
||||
*MethodDrillOutcomePreferenceLine
|
||||
);
|
||||
MethodDrillDiagnosticsDetailLine = FString::Printf(
|
||||
TEXT("%s || History match %d | strong %d | weak %d | avg success %.2f | avg mistakes %.2f"),
|
||||
*MethodDrillDiagnosticsDetailLine,
|
||||
MethodDrillFollowUpWeighting.MatchingHistoryCount,
|
||||
MethodDrillFollowUpWeighting.StrongOutcomeCount,
|
||||
MethodDrillFollowUpWeighting.WeakOutcomeCount,
|
||||
MethodDrillFollowUpWeighting.AverageSuccessRate,
|
||||
MethodDrillFollowUpWeighting.AverageMistakes
|
||||
);
|
||||
}
|
||||
if (bCanLaunchMethodDrillFollowUp && MethodDrillFollowUpWeighting.bPreferDrillFollowUp)
|
||||
{
|
||||
StartMethodDrillFollowUpLabel = TEXT("Launch Preferred Drill Follow-Up");
|
||||
}
|
||||
else if (bCanLaunchMethodDrillFollowUp && MethodDrillFollowUpWeighting.bPreferCoachHandoff)
|
||||
{
|
||||
StartMethodDrillFollowUpLabel = TEXT("Drill Follow-Up Optional");
|
||||
}
|
||||
const bool bHasCurrentRunRecap = bHasActiveRun
|
||||
&& (CachedRunState.Session.SessionState == EHyperTwistTrainingSessionState::Completed
|
||||
|| CachedRunState.Session.SessionState == EHyperTwistTrainingSessionState::Aborted);
|
||||
|
|
@ -5716,6 +6079,14 @@ void UHyperTwistCoachDashboardWidget::UpdateDashboardPresentation()
|
|||
HandoffHistoryBias.FollowUpRecommendationCount,
|
||||
HandoffHistoryBias.RecommendedRecommendationCount
|
||||
);
|
||||
if (bCanLaunchMethodDrillFollowUp && MethodDrillFollowUpWeighting.IsStructurallyValid())
|
||||
{
|
||||
CoachHandoffRecommendationDetailLine += MethodDrillFollowUpWeighting.bPreferDrillFollowUp
|
||||
? TEXT(" | Drill history says stay narrow with drill-derived follow-up before broader coach handoff.")
|
||||
: (MethodDrillFollowUpWeighting.bPreferCoachHandoff
|
||||
? TEXT(" | Drill history says broader coach handoff is now stronger than another narrow drill follow-up.")
|
||||
: TEXT(" | Drill history is balanced against broader coach handoff."));
|
||||
}
|
||||
}
|
||||
RecordCoachHandoffHistorySnapshot(
|
||||
HyperTwistCoachDashboardWidgetInternal::DescribeHandoffRecommendationKind(
|
||||
|
|
@ -6897,6 +7268,41 @@ void UHyperTwistCoachDashboardWidget::UpdateDashboardPresentation()
|
|||
{
|
||||
StartMethodDrillFollowUpButton->SetIsEnabled(bCanLaunchMethodDrillFollowUp);
|
||||
}
|
||||
if (MethodDrillFollowUpHistoryHeaderTextBlock != nullptr)
|
||||
{
|
||||
MethodDrillFollowUpHistoryHeaderTextBlock->SetText(FText::FromString(
|
||||
TEXT("[Method Drill Follow-Up History]")));
|
||||
}
|
||||
if (MethodDrillFollowUpHistoryStatusTextBlock != nullptr)
|
||||
{
|
||||
MethodDrillFollowUpHistoryStatusTextBlock->SetText(FText::FromString(
|
||||
MethodDrillFollowUpHistoryStatusLine));
|
||||
}
|
||||
if (MethodDrillFollowUpHistoryDetailTextBlock != nullptr)
|
||||
{
|
||||
MethodDrillFollowUpHistoryDetailTextBlock->SetText(FText::FromString(
|
||||
MethodDrillFollowUpHistoryDetailLine));
|
||||
}
|
||||
if (PreviousMethodDrillFollowUpHistoryButtonLabel != nullptr)
|
||||
{
|
||||
PreviousMethodDrillFollowUpHistoryButtonLabel->SetText(FText::FromString(
|
||||
TEXT("Prev Drill Follow-Up")));
|
||||
}
|
||||
if (PreviousMethodDrillFollowUpHistoryButton != nullptr)
|
||||
{
|
||||
PreviousMethodDrillFollowUpHistoryButton->SetIsEnabled(
|
||||
MethodDrillFollowUpHistoryEntries.Num() > 1);
|
||||
}
|
||||
if (NextMethodDrillFollowUpHistoryButtonLabel != nullptr)
|
||||
{
|
||||
NextMethodDrillFollowUpHistoryButtonLabel->SetText(FText::FromString(
|
||||
TEXT("Next Drill Follow-Up")));
|
||||
}
|
||||
if (NextMethodDrillFollowUpHistoryButton != nullptr)
|
||||
{
|
||||
NextMethodDrillFollowUpHistoryButton->SetIsEnabled(
|
||||
MethodDrillFollowUpHistoryEntries.Num() > 1);
|
||||
}
|
||||
if (StartQueuedRunButtonLabel != nullptr)
|
||||
{
|
||||
StartQueuedRunButtonLabel->SetText(FText::FromString(QueuedRunLabel));
|
||||
|
|
|
|||
|
|
@ -267,6 +267,57 @@ struct FHyperTwistCoachDashboardHandoffHistoryEntry
|
|||
}
|
||||
};
|
||||
|
||||
struct FHyperTwistCoachDashboardMethodDrillFollowUpHistoryEntry
|
||||
{
|
||||
FString RecordedAtUtc;
|
||||
FString SourceRunId;
|
||||
FString SourceDrillId;
|
||||
FString SourceSessionId;
|
||||
FString StageLabel;
|
||||
EHyperTwistTrainingMethodDrillDiagnosticState DiagnosticState =
|
||||
EHyperTwistTrainingMethodDrillDiagnosticState::Unknown;
|
||||
EHyperTwistTrainingReviewGrade RecommendedReviewGrade = EHyperTwistTrainingReviewGrade::Good;
|
||||
EHyperTwistTrainingSessionTemplateKind TemplateKind =
|
||||
EHyperTwistTrainingSessionTemplateKind::ReviewPlan;
|
||||
EHyperTwistTrainingDeliveryMode SuggestedMode = EHyperTwistTrainingDeliveryMode::Timer;
|
||||
EHyperTwistTrainingSelectionPolicy SuggestedSelectionPolicy =
|
||||
EHyperTwistTrainingSelectionPolicy::Weighted;
|
||||
int32 FocusCaseCount = 0;
|
||||
int32 LaunchedCaseCount = 0;
|
||||
int32 CompletedAttemptCount = 0;
|
||||
int32 TotalMistakes = 0;
|
||||
float SuccessRate = 0.0f;
|
||||
bool bBoundaryPending = false;
|
||||
bool bBoundaryRepeatBurden = false;
|
||||
bool bBoundaryAssistance = false;
|
||||
|
||||
bool IsStructurallyValid() const
|
||||
{
|
||||
return !RecordedAtUtc.IsEmpty()
|
||||
&& !SourceRunId.IsEmpty()
|
||||
&& !SourceDrillId.IsEmpty()
|
||||
&& !SourceSessionId.IsEmpty();
|
||||
}
|
||||
};
|
||||
|
||||
struct FHyperTwistCoachDashboardMethodDrillFollowUpWeighting
|
||||
{
|
||||
int32 MatchingHistoryCount = 0;
|
||||
int32 StrongOutcomeCount = 0;
|
||||
int32 WeakOutcomeCount = 0;
|
||||
int32 ContinueNarrowBias = 0;
|
||||
int32 RedirectCoachBias = 0;
|
||||
float AverageSuccessRate = 0.0f;
|
||||
float AverageMistakes = 0.0f;
|
||||
bool bPreferDrillFollowUp = false;
|
||||
bool bPreferCoachHandoff = false;
|
||||
|
||||
bool IsStructurallyValid() const
|
||||
{
|
||||
return MatchingHistoryCount > 0;
|
||||
}
|
||||
};
|
||||
|
||||
UCLASS(BlueprintType, Blueprintable)
|
||||
class UNREALHYPERTWIST_API UHyperTwistCoachDashboardWidget : public UHyperTwistTrainingPanelWidget
|
||||
{
|
||||
|
|
@ -318,6 +369,9 @@ public:
|
|||
UPROPERTY(VisibleAnywhere, BlueprintReadOnly, Category = "HyperTwist|Training|Coach|Dashboard")
|
||||
int32 SelectedClosureRecoveryIndex = INDEX_NONE;
|
||||
|
||||
UPROPERTY(VisibleAnywhere, BlueprintReadOnly, Category = "HyperTwist|Training|Coach|Dashboard")
|
||||
int32 SelectedMethodDrillFollowUpHistoryIndex = INDEX_NONE;
|
||||
|
||||
UPROPERTY(VisibleAnywhere, BlueprintReadOnly, Category = "HyperTwist|Training|Coach|Dashboard")
|
||||
bool bHasLiveAttemptTimer = false;
|
||||
|
||||
|
|
@ -423,6 +477,12 @@ public:
|
|||
UFUNCTION(BlueprintCallable, Category = "HyperTwist|Training|Coach|Dashboard")
|
||||
bool SelectNextClosureRecoveryHistoryEntry();
|
||||
|
||||
UFUNCTION(BlueprintCallable, Category = "HyperTwist|Training|Coach|Dashboard")
|
||||
bool SelectPreviousMethodDrillFollowUpHistoryEntry();
|
||||
|
||||
UFUNCTION(BlueprintCallable, Category = "HyperTwist|Training|Coach|Dashboard")
|
||||
bool SelectNextMethodDrillFollowUpHistoryEntry();
|
||||
|
||||
UFUNCTION(BlueprintCallable, Category = "HyperTwist|Training|Coach|Dashboard")
|
||||
bool StartLiveAttemptTimer();
|
||||
|
||||
|
|
@ -612,6 +672,27 @@ protected:
|
|||
UPROPERTY(Transient)
|
||||
TObjectPtr<UTextBlock> StartMethodDrillFollowUpButtonLabel = nullptr;
|
||||
|
||||
UPROPERTY(Transient)
|
||||
TObjectPtr<UTextBlock> MethodDrillFollowUpHistoryHeaderTextBlock = nullptr;
|
||||
|
||||
UPROPERTY(Transient)
|
||||
TObjectPtr<UTextBlock> MethodDrillFollowUpHistoryStatusTextBlock = nullptr;
|
||||
|
||||
UPROPERTY(Transient)
|
||||
TObjectPtr<UTextBlock> MethodDrillFollowUpHistoryDetailTextBlock = nullptr;
|
||||
|
||||
UPROPERTY(Transient)
|
||||
TObjectPtr<UButton> PreviousMethodDrillFollowUpHistoryButton = nullptr;
|
||||
|
||||
UPROPERTY(Transient)
|
||||
TObjectPtr<UTextBlock> PreviousMethodDrillFollowUpHistoryButtonLabel = nullptr;
|
||||
|
||||
UPROPERTY(Transient)
|
||||
TObjectPtr<UButton> NextMethodDrillFollowUpHistoryButton = nullptr;
|
||||
|
||||
UPROPERTY(Transient)
|
||||
TObjectPtr<UTextBlock> NextMethodDrillFollowUpHistoryButtonLabel = nullptr;
|
||||
|
||||
UPROPERTY(Transient)
|
||||
TObjectPtr<UButton> RepeatRunButton = nullptr;
|
||||
|
||||
|
|
@ -1050,6 +1131,12 @@ protected:
|
|||
UFUNCTION()
|
||||
void HandleNextClosureRecoveryClicked();
|
||||
|
||||
UFUNCTION()
|
||||
void HandlePreviousMethodDrillFollowUpHistoryClicked();
|
||||
|
||||
UFUNCTION()
|
||||
void HandleNextMethodDrillFollowUpHistoryClicked();
|
||||
|
||||
UFUNCTION()
|
||||
void HandleClearRunClicked();
|
||||
|
||||
|
|
@ -1108,6 +1195,7 @@ private:
|
|||
void SyncSelectedHandoffHistoryEntry();
|
||||
void SyncSelectedQueueSuppressionHistoryEntry();
|
||||
void SyncSelectedClosureRecoveryHistoryEntry();
|
||||
void SyncSelectedMethodDrillFollowUpHistoryEntry();
|
||||
void SyncPreferredGuidancePreviewLane(bool bForceAdoptPreferredLane);
|
||||
void UpdateDashboardPresentation();
|
||||
void RefreshLiveAttemptClock();
|
||||
|
|
@ -1136,6 +1224,7 @@ private:
|
|||
) const;
|
||||
FHyperTwistCoachDashboardGuidanceLanePreference BuildGuidanceLanePreference() const;
|
||||
FHyperTwistCoachDashboardQueueRecoveryWeighting BuildQueueRecoveryWeighting() const;
|
||||
FHyperTwistCoachDashboardMethodDrillFollowUpWeighting BuildMethodDrillFollowUpWeighting() const;
|
||||
FString BuildGuidanceOutcomeActionLabel(EHyperTwistCoachDashboardOutcomeAction Action) const;
|
||||
FHyperTwistTrainingDeck BuildGuidanceOutcomeSelectedCaseDeck() const;
|
||||
void RecordQueueRecoveryOutcome(
|
||||
|
|
@ -1164,6 +1253,7 @@ private:
|
|||
);
|
||||
void RecordQueueSuppressionSnapshot();
|
||||
void RecordClosureRecoverySnapshot();
|
||||
void RecordMethodDrillFollowUpHistoryEntry(const FHyperTwistTrainingSessionSummary& CompletedRunRecap);
|
||||
void RecordGuidanceOutcomeDecision(
|
||||
EHyperTwistCoachDashboardOutcomeAction RecommendedAction,
|
||||
EHyperTwistCoachDashboardOutcomeAction AppliedAction,
|
||||
|
|
@ -1181,6 +1271,7 @@ private:
|
|||
bool bSingleCaseLaunch,
|
||||
const FString& SelectedCaseId
|
||||
);
|
||||
void CaptureActiveMethodDrillFollowUpLaunchContext(const FString& SessionId);
|
||||
bool IsLiveAttemptInspectionPhase() const;
|
||||
int32 GetMaxManualSplitCaptureCount() const;
|
||||
const FHyperTwistTrainingSplitPhaseDefinition* FindNextSplitPhaseToCapture() const;
|
||||
|
|
@ -1203,6 +1294,9 @@ private:
|
|||
FHyperTwistCoachBrief ActiveGuidanceLaunchBrief;
|
||||
FHyperTwistTrainingDeck ActiveGuidanceLaunchDeck;
|
||||
FString ActiveGuidanceLaunchSelectedCaseId;
|
||||
FString ActiveMethodDrillFollowUpLaunchSessionId;
|
||||
FHyperTwistTrainingMethodDrillDiagnosticPacket ActiveMethodDrillFollowUpLaunchDiagnosticPacket;
|
||||
FHyperTwistTrainingSessionTemplate ActiveMethodDrillFollowUpLaunchTemplate;
|
||||
bool bHasRetainedGuidanceComparison = false;
|
||||
FString RetainedGuidanceComparisonSessionId;
|
||||
EHyperTwistCoachDashboardGuidancePreviewLane RetainedGuidanceComparisonLane =
|
||||
|
|
@ -1213,6 +1307,7 @@ private:
|
|||
FString RetainedGuidanceComparisonSelectedCaseId;
|
||||
TArray<FHyperTwistCoachDashboardDecisionHistoryEntry> GuidanceDecisionHistoryEntries;
|
||||
TArray<FHyperTwistCoachDashboardHandoffHistoryEntry> HandoffHistoryEntries;
|
||||
TArray<FHyperTwistCoachDashboardMethodDrillFollowUpHistoryEntry> MethodDrillFollowUpHistoryEntries;
|
||||
bool bHasQueueRecoveryOutcome = false;
|
||||
FHyperTwistCoachDashboardQueueRecoveryOutcome QueueRecoveryOutcome;
|
||||
TArray<FHyperTwistCoachDashboardQueueRecoveryOutcome> QueueRecoveryOutcomeHistory;
|
||||
|
|
|
|||
Loading…
Add table
Reference in a new issue