Retain coach handoff history on dashboard
This commit is contained in:
parent
d0a55eca5a
commit
69cb604fcd
2 changed files with 424 additions and 29 deletions
|
|
@ -39,6 +39,34 @@ namespace HyperTwistCoachDashboardWidgetInternal
|
|||
return TEXT("Unknown");
|
||||
}
|
||||
|
||||
FString DescribeGuidanceLane(const EHyperTwistTrainingCoachGuidanceLane Lane)
|
||||
{
|
||||
switch (Lane)
|
||||
{
|
||||
case EHyperTwistTrainingCoachGuidanceLane::FollowUp:
|
||||
return TEXT("follow-up");
|
||||
case EHyperTwistTrainingCoachGuidanceLane::Recommended:
|
||||
return TEXT("recommended");
|
||||
default:
|
||||
return TEXT("none");
|
||||
}
|
||||
}
|
||||
|
||||
FString DescribeHandoffRecommendationKind(const EHandoffRecommendationKind Kind)
|
||||
{
|
||||
switch (Kind)
|
||||
{
|
||||
case EHandoffRecommendationKind::Queue:
|
||||
return TEXT("queue");
|
||||
case EHandoffRecommendationKind::FollowUp:
|
||||
return TEXT("follow-up");
|
||||
case EHandoffRecommendationKind::Recommended:
|
||||
return TEXT("recommended");
|
||||
default:
|
||||
return TEXT("none");
|
||||
}
|
||||
}
|
||||
|
||||
bool IsDashboardHandoffGuidanceSource(const FString& SourceLabel)
|
||||
{
|
||||
return SourceLabel.StartsWith(TEXT("coach-memory-dashboard-"));
|
||||
|
|
@ -300,6 +328,7 @@ void UHyperTwistCoachDashboardWidget::RefreshCoachDashboardView()
|
|||
SyncSelectedQueueEntry();
|
||||
SyncSelectedAttempt();
|
||||
SyncSelectedGuidanceDecisionHistoryEntry();
|
||||
SyncSelectedHandoffHistoryEntry();
|
||||
SyncSelectedQueueSuppressionHistoryEntry();
|
||||
SyncSelectedClosureRecoveryHistoryEntry();
|
||||
SyncPreferredGuidancePreviewLane(false);
|
||||
|
|
@ -1102,6 +1131,46 @@ bool UHyperTwistCoachDashboardWidget::SelectNextGuidanceDecisionHistoryEntry()
|
|||
return true;
|
||||
}
|
||||
|
||||
bool UHyperTwistCoachDashboardWidget::SelectPreviousHandoffHistoryEntry()
|
||||
{
|
||||
if (HandoffHistoryEntries.Num() <= 0)
|
||||
{
|
||||
SelectedHandoffHistoryIndex = INDEX_NONE;
|
||||
return false;
|
||||
}
|
||||
|
||||
if (SelectedHandoffHistoryIndex == INDEX_NONE)
|
||||
{
|
||||
SyncSelectedHandoffHistoryEntry();
|
||||
return SelectedHandoffHistoryIndex != INDEX_NONE;
|
||||
}
|
||||
|
||||
SelectedHandoffHistoryIndex =
|
||||
(SelectedHandoffHistoryIndex - 1 + HandoffHistoryEntries.Num()) % HandoffHistoryEntries.Num();
|
||||
UpdateDashboardPresentation();
|
||||
return true;
|
||||
}
|
||||
|
||||
bool UHyperTwistCoachDashboardWidget::SelectNextHandoffHistoryEntry()
|
||||
{
|
||||
if (HandoffHistoryEntries.Num() <= 0)
|
||||
{
|
||||
SelectedHandoffHistoryIndex = INDEX_NONE;
|
||||
return false;
|
||||
}
|
||||
|
||||
if (SelectedHandoffHistoryIndex == INDEX_NONE)
|
||||
{
|
||||
SyncSelectedHandoffHistoryEntry();
|
||||
return SelectedHandoffHistoryIndex != INDEX_NONE;
|
||||
}
|
||||
|
||||
SelectedHandoffHistoryIndex =
|
||||
(SelectedHandoffHistoryIndex + 1) % HandoffHistoryEntries.Num();
|
||||
UpdateDashboardPresentation();
|
||||
return true;
|
||||
}
|
||||
|
||||
bool UHyperTwistCoachDashboardWidget::SelectPreviousQueueSuppressionHistoryEntry()
|
||||
{
|
||||
if (QueueSuppressionHistoryEntries.Num() <= 0)
|
||||
|
|
@ -1469,6 +1538,16 @@ void UHyperTwistCoachDashboardWidget::HandleNextGuidanceDecisionClicked()
|
|||
SelectNextGuidanceDecisionHistoryEntry();
|
||||
}
|
||||
|
||||
void UHyperTwistCoachDashboardWidget::HandlePreviousHandoffHistoryClicked()
|
||||
{
|
||||
SelectPreviousHandoffHistoryEntry();
|
||||
}
|
||||
|
||||
void UHyperTwistCoachDashboardWidget::HandleNextHandoffHistoryClicked()
|
||||
{
|
||||
SelectNextHandoffHistoryEntry();
|
||||
}
|
||||
|
||||
void UHyperTwistCoachDashboardWidget::HandlePreviousQueueSuppressionClicked()
|
||||
{
|
||||
SelectPreviousQueueSuppressionHistoryEntry();
|
||||
|
|
@ -1826,6 +1905,24 @@ void UHyperTwistCoachDashboardWidget::EnsureDefaultDashboardBuilt()
|
|||
TEXT("CoachHandoffRecommendationDetail"),
|
||||
8
|
||||
);
|
||||
HandoffHistoryHeaderTextBlock = HyperTwistCoachDashboardWidgetInternal::AddTextRow(
|
||||
WidgetTree,
|
||||
RootLayout,
|
||||
TEXT("CoachHandoffHistoryHeader"),
|
||||
2
|
||||
);
|
||||
HandoffHistoryStatusTextBlock = HyperTwistCoachDashboardWidgetInternal::AddTextRow(
|
||||
WidgetTree,
|
||||
RootLayout,
|
||||
TEXT("CoachHandoffHistoryStatus"),
|
||||
2
|
||||
);
|
||||
HandoffHistoryDetailTextBlock = HyperTwistCoachDashboardWidgetInternal::AddTextRow(
|
||||
WidgetTree,
|
||||
RootLayout,
|
||||
TEXT("CoachHandoffHistoryDetail"),
|
||||
8
|
||||
);
|
||||
GuidanceReviewHeaderTextBlock = HyperTwistCoachDashboardWidgetInternal::AddTextRow(
|
||||
WidgetTree,
|
||||
RootLayout,
|
||||
|
|
@ -1984,6 +2081,39 @@ void UHyperTwistCoachDashboardWidget::EnsureDefaultDashboardBuilt()
|
|||
);
|
||||
StartRecommendedRunButtonLabel = StartRecommendedRunLabelRaw;
|
||||
}
|
||||
UHorizontalBox* HandoffHistoryNavRow = WidgetTree->ConstructWidget<UHorizontalBox>(
|
||||
UHorizontalBox::StaticClass(),
|
||||
TEXT("CoachDashboardHandoffHistoryNavRow")
|
||||
);
|
||||
if (HandoffHistoryNavRow != nullptr)
|
||||
{
|
||||
if (UVerticalBoxSlot* HandoffHistoryNavRowSlot = RootLayout->AddChildToVerticalBox(HandoffHistoryNavRow))
|
||||
{
|
||||
HandoffHistoryNavRowSlot->SetPadding(FMargin(0.0f, 4.0f, 0.0f, 0.0f));
|
||||
}
|
||||
|
||||
UTextBlock* PreviousHandoffHistoryLabelRaw = nullptr;
|
||||
PreviousHandoffHistoryButton = HyperTwistCoachDashboardWidgetInternal::AddButton(
|
||||
WidgetTree,
|
||||
HandoffHistoryNavRow,
|
||||
TEXT("PreviousHandoffHistoryButton"),
|
||||
TEXT("PreviousHandoffHistoryButtonLabel"),
|
||||
TEXT("Prev Handoff"),
|
||||
PreviousHandoffHistoryLabelRaw
|
||||
);
|
||||
PreviousHandoffHistoryButtonLabel = PreviousHandoffHistoryLabelRaw;
|
||||
|
||||
UTextBlock* NextHandoffHistoryLabelRaw = nullptr;
|
||||
NextHandoffHistoryButton = HyperTwistCoachDashboardWidgetInternal::AddButton(
|
||||
WidgetTree,
|
||||
HandoffHistoryNavRow,
|
||||
TEXT("NextHandoffHistoryButton"),
|
||||
TEXT("NextHandoffHistoryButtonLabel"),
|
||||
TEXT("Next Handoff"),
|
||||
NextHandoffHistoryLabelRaw
|
||||
);
|
||||
NextHandoffHistoryButtonLabel = NextHandoffHistoryLabelRaw;
|
||||
}
|
||||
UHorizontalBox* GuidancePreviewActionRow = WidgetTree->ConstructWidget<UHorizontalBox>(
|
||||
UHorizontalBox::StaticClass(),
|
||||
TEXT("CoachDashboardGuidancePreviewActions")
|
||||
|
|
@ -2787,6 +2917,14 @@ void UHyperTwistCoachDashboardWidget::EnsureDefaultDashboardBuilt()
|
|||
{
|
||||
NextGuidanceDecisionButton->OnClicked.AddDynamic(this, &UHyperTwistCoachDashboardWidget::HandleNextGuidanceDecisionClicked);
|
||||
}
|
||||
if (PreviousHandoffHistoryButton != nullptr)
|
||||
{
|
||||
PreviousHandoffHistoryButton->OnClicked.AddDynamic(this, &UHyperTwistCoachDashboardWidget::HandlePreviousHandoffHistoryClicked);
|
||||
}
|
||||
if (NextHandoffHistoryButton != nullptr)
|
||||
{
|
||||
NextHandoffHistoryButton->OnClicked.AddDynamic(this, &UHyperTwistCoachDashboardWidget::HandleNextHandoffHistoryClicked);
|
||||
}
|
||||
if (DismissRecapButton != nullptr)
|
||||
{
|
||||
DismissRecapButton->OnClicked.AddDynamic(this, &UHyperTwistCoachDashboardWidget::HandleDismissRecapClicked);
|
||||
|
|
@ -3038,6 +3176,21 @@ void UHyperTwistCoachDashboardWidget::SyncSelectedGuidanceDecisionHistoryEntry()
|
|||
}
|
||||
}
|
||||
|
||||
void UHyperTwistCoachDashboardWidget::SyncSelectedHandoffHistoryEntry()
|
||||
{
|
||||
if (HandoffHistoryEntries.Num() <= 0)
|
||||
{
|
||||
SelectedHandoffHistoryIndex = INDEX_NONE;
|
||||
return;
|
||||
}
|
||||
|
||||
if (SelectedHandoffHistoryIndex < 0
|
||||
|| SelectedHandoffHistoryIndex >= HandoffHistoryEntries.Num())
|
||||
{
|
||||
SelectedHandoffHistoryIndex = HandoffHistoryEntries.Num() - 1;
|
||||
}
|
||||
}
|
||||
|
||||
void UHyperTwistCoachDashboardWidget::SyncSelectedQueueSuppressionHistoryEntry()
|
||||
{
|
||||
if (QueueSuppressionHistoryEntries.Num() <= 0)
|
||||
|
|
@ -3914,6 +4067,88 @@ void UHyperTwistCoachDashboardWidget::RecordQueueRecoveryOutcome(
|
|||
}
|
||||
}
|
||||
|
||||
void UHyperTwistCoachDashboardWidget::RecordCoachHandoffHistorySnapshot(
|
||||
const FString& RecommendedHandoffKindLabel,
|
||||
const FString& RecommendationStatusLine,
|
||||
const FString& RecommendationDetailLine,
|
||||
const FString& QueueSourceLabel,
|
||||
const int32 ClosureContinuationBias,
|
||||
const int32 ClosureSuppressionBias,
|
||||
const bool bQueueReady,
|
||||
const bool bFollowUpReady,
|
||||
const bool bRecommendedReady)
|
||||
{
|
||||
FHyperTwistCoachDashboardHandoffHistoryEntry Entry;
|
||||
Entry.RecordedAtUtc = FDateTime::UtcNow().ToIso8601();
|
||||
Entry.RecommendedHandoffKindLabel =
|
||||
RecommendedHandoffKindLabel.IsEmpty() ? TEXT("none") : RecommendedHandoffKindLabel;
|
||||
Entry.RecommendationStatusLine = RecommendationStatusLine;
|
||||
Entry.RecommendationDetailLine = RecommendationDetailLine;
|
||||
Entry.QueueSourceLabel = QueueSourceLabel;
|
||||
Entry.PreferredGuidanceLane = CachedCoachPanelState.PreferredGuidanceLane;
|
||||
Entry.PreferredGuidanceSourceLabel = CachedCoachPanelState.PreferredGuidanceSourceLabel;
|
||||
Entry.PreferredRecoveryAction = CachedCoachPanelState.PreferredQueueRecoveryAction;
|
||||
Entry.PreferredRecoveryActionSourceLabel =
|
||||
CachedCoachPanelState.PreferredQueueRecoveryActionSourceLabel;
|
||||
Entry.AcceptedQueuedHandoffCount =
|
||||
CachedCoachQueueExecutionSummary.StartedQueuedHandoffEventCount;
|
||||
Entry.AcceptedFollowUpHandoffCount =
|
||||
CachedCoachQueueExecutionSummary.StartedFollowUpHandoffEventCount;
|
||||
Entry.AcceptedRecommendedHandoffCount =
|
||||
CachedCoachQueueExecutionSummary.StartedRecommendedHandoffEventCount;
|
||||
Entry.ClosureContinuationBias = ClosureContinuationBias;
|
||||
Entry.ClosureSuppressionBias = ClosureSuppressionBias;
|
||||
Entry.bQueueReady = bQueueReady;
|
||||
Entry.bFollowUpReady = bFollowUpReady;
|
||||
Entry.bRecommendedReady = bRecommendedReady;
|
||||
if (!Entry.IsStructurallyValid())
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
const bool bMatchesLastEntry = HandoffHistoryEntries.Num() > 0
|
||||
&& HandoffHistoryEntries.Last().RecommendedHandoffKindLabel
|
||||
== Entry.RecommendedHandoffKindLabel
|
||||
&& HandoffHistoryEntries.Last().RecommendationStatusLine
|
||||
== Entry.RecommendationStatusLine
|
||||
&& HandoffHistoryEntries.Last().RecommendationDetailLine
|
||||
== Entry.RecommendationDetailLine
|
||||
&& HandoffHistoryEntries.Last().QueueSourceLabel == Entry.QueueSourceLabel
|
||||
&& HandoffHistoryEntries.Last().PreferredGuidanceLane == Entry.PreferredGuidanceLane
|
||||
&& HandoffHistoryEntries.Last().PreferredGuidanceSourceLabel
|
||||
== Entry.PreferredGuidanceSourceLabel
|
||||
&& HandoffHistoryEntries.Last().PreferredRecoveryAction
|
||||
== Entry.PreferredRecoveryAction
|
||||
&& HandoffHistoryEntries.Last().PreferredRecoveryActionSourceLabel
|
||||
== Entry.PreferredRecoveryActionSourceLabel
|
||||
&& HandoffHistoryEntries.Last().AcceptedQueuedHandoffCount
|
||||
== Entry.AcceptedQueuedHandoffCount
|
||||
&& HandoffHistoryEntries.Last().AcceptedFollowUpHandoffCount
|
||||
== Entry.AcceptedFollowUpHandoffCount
|
||||
&& HandoffHistoryEntries.Last().AcceptedRecommendedHandoffCount
|
||||
== Entry.AcceptedRecommendedHandoffCount
|
||||
&& HandoffHistoryEntries.Last().ClosureContinuationBias
|
||||
== Entry.ClosureContinuationBias
|
||||
&& HandoffHistoryEntries.Last().ClosureSuppressionBias
|
||||
== Entry.ClosureSuppressionBias
|
||||
&& HandoffHistoryEntries.Last().bQueueReady == Entry.bQueueReady
|
||||
&& HandoffHistoryEntries.Last().bFollowUpReady == Entry.bFollowUpReady
|
||||
&& HandoffHistoryEntries.Last().bRecommendedReady == Entry.bRecommendedReady;
|
||||
if (bMatchesLastEntry)
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
HandoffHistoryEntries.Add(Entry);
|
||||
constexpr int32 MaxRetainedHandoffHistoryEntries = 16;
|
||||
if (HandoffHistoryEntries.Num() > MaxRetainedHandoffHistoryEntries)
|
||||
{
|
||||
const int32 ExcessCount = HandoffHistoryEntries.Num() - MaxRetainedHandoffHistoryEntries;
|
||||
HandoffHistoryEntries.RemoveAt(0, ExcessCount, EAllowShrinking::No);
|
||||
}
|
||||
SelectedHandoffHistoryIndex = HandoffHistoryEntries.Num() - 1;
|
||||
}
|
||||
|
||||
void UHyperTwistCoachDashboardWidget::RecordQueueSuppressionSnapshot()
|
||||
{
|
||||
const bool bHasPrimarySuppression =
|
||||
|
|
@ -4369,6 +4604,12 @@ void UHyperTwistCoachDashboardWidget::UpdateDashboardPresentation()
|
|||
HyperTwistCoachDashboardWidgetInternal::EHandoffRecommendationKind::None;
|
||||
FString CoachHandoffRecommendationStatusLine;
|
||||
FString CoachHandoffRecommendationDetailLine;
|
||||
const bool bQueueReadyForHandoff = CachedCoachPanelState.bCanStartQueuedRun;
|
||||
const bool bFollowUpReadyForHandoff =
|
||||
CachedCoachPanelState.bCanStartCoachFollowUpRun
|
||||
&& CachedCoachPanelState.bHasFollowUpGuidance;
|
||||
const bool bRecommendedReadyForHandoff =
|
||||
CachedCoachPanelState.bCanStartCoachRecommendedRun;
|
||||
if (!bCanUseCoachHandoff)
|
||||
{
|
||||
CoachHandoffRecommendationStatusLine =
|
||||
|
|
@ -4383,13 +4624,12 @@ void UHyperTwistCoachDashboardWidget::UpdateDashboardPresentation()
|
|||
if (HandoffClosureBias.bHasSignal
|
||||
&& HandoffClosureBias.ContinuationBias >= HandoffClosureBias.SuppressionBias + 2)
|
||||
{
|
||||
if (CachedCoachPanelState.bCanStartQueuedRun && bQueueLooksFollowUpOrRecovery)
|
||||
if (bQueueReadyForHandoff && bQueueLooksFollowUpOrRecovery)
|
||||
{
|
||||
HandoffRecommendationKind =
|
||||
HyperTwistCoachDashboardWidgetInternal::EHandoffRecommendationKind::Queue;
|
||||
}
|
||||
else if (CachedCoachPanelState.bCanStartCoachFollowUpRun
|
||||
&& CachedCoachPanelState.bHasFollowUpGuidance)
|
||||
else if (bFollowUpReadyForHandoff)
|
||||
{
|
||||
HandoffRecommendationKind =
|
||||
HyperTwistCoachDashboardWidgetInternal::EHandoffRecommendationKind::FollowUp;
|
||||
|
|
@ -4398,12 +4638,12 @@ void UHyperTwistCoachDashboardWidget::UpdateDashboardPresentation()
|
|||
else if (HandoffClosureBias.bHasSignal
|
||||
&& HandoffClosureBias.SuppressionBias >= HandoffClosureBias.ContinuationBias + 2)
|
||||
{
|
||||
if (CachedCoachPanelState.bCanStartQueuedRun && bQueueLooksRecommendedOrPrimary)
|
||||
if (bQueueReadyForHandoff && bQueueLooksRecommendedOrPrimary)
|
||||
{
|
||||
HandoffRecommendationKind =
|
||||
HyperTwistCoachDashboardWidgetInternal::EHandoffRecommendationKind::Queue;
|
||||
}
|
||||
else if (CachedCoachPanelState.bCanStartCoachRecommendedRun)
|
||||
else if (bRecommendedReadyForHandoff)
|
||||
{
|
||||
HandoffRecommendationKind =
|
||||
HyperTwistCoachDashboardWidgetInternal::EHandoffRecommendationKind::Recommended;
|
||||
|
|
@ -4413,18 +4653,17 @@ void UHyperTwistCoachDashboardWidget::UpdateDashboardPresentation()
|
|||
if (HandoffRecommendationKind
|
||||
== HyperTwistCoachDashboardWidgetInternal::EHandoffRecommendationKind::None)
|
||||
{
|
||||
if (CachedCoachPanelState.bCanStartQueuedRun)
|
||||
if (bQueueReadyForHandoff)
|
||||
{
|
||||
HandoffRecommendationKind =
|
||||
HyperTwistCoachDashboardWidgetInternal::EHandoffRecommendationKind::Queue;
|
||||
}
|
||||
else if (CachedCoachPanelState.bCanStartCoachFollowUpRun
|
||||
&& CachedCoachPanelState.bHasFollowUpGuidance)
|
||||
else if (bFollowUpReadyForHandoff)
|
||||
{
|
||||
HandoffRecommendationKind =
|
||||
HyperTwistCoachDashboardWidgetInternal::EHandoffRecommendationKind::FollowUp;
|
||||
}
|
||||
else if (CachedCoachPanelState.bCanStartCoachRecommendedRun)
|
||||
else if (bRecommendedReadyForHandoff)
|
||||
{
|
||||
HandoffRecommendationKind =
|
||||
HyperTwistCoachDashboardWidgetInternal::EHandoffRecommendationKind::Recommended;
|
||||
|
|
@ -4461,23 +4700,29 @@ void UHyperTwistCoachDashboardWidget::UpdateDashboardPresentation()
|
|||
TEXT("Closure continuation bias %d | Closure suppression bias %d | Queue ready: %s | Follow-up ready: %s | Recommended ready: %s | Preferred lane: %s | Memory source: %s%s"),
|
||||
HandoffClosureBias.ContinuationBias,
|
||||
HandoffClosureBias.SuppressionBias,
|
||||
CachedCoachPanelState.bCanStartQueuedRun ? TEXT("yes") : TEXT("no"),
|
||||
(CachedCoachPanelState.bCanStartCoachFollowUpRun && CachedCoachPanelState.bHasFollowUpGuidance)
|
||||
? TEXT("yes")
|
||||
: TEXT("no"),
|
||||
CachedCoachPanelState.bCanStartCoachRecommendedRun ? TEXT("yes") : TEXT("no"),
|
||||
CachedCoachPanelState.PreferredGuidanceLane == EHyperTwistTrainingCoachGuidanceLane::FollowUp
|
||||
? TEXT("follow-up")
|
||||
: (CachedCoachPanelState.PreferredGuidanceLane
|
||||
== EHyperTwistTrainingCoachGuidanceLane::Recommended
|
||||
? TEXT("recommended")
|
||||
: TEXT("none")),
|
||||
bQueueReadyForHandoff ? TEXT("yes") : TEXT("no"),
|
||||
bFollowUpReadyForHandoff ? TEXT("yes") : TEXT("no"),
|
||||
bRecommendedReadyForHandoff ? TEXT("yes") : TEXT("no"),
|
||||
*HyperTwistCoachDashboardWidgetInternal::DescribeGuidanceLane(
|
||||
CachedCoachPanelState.PreferredGuidanceLane),
|
||||
*HandoffMemorySourceLabel,
|
||||
bDashboardHandoffMemorySource
|
||||
? TEXT(" | Accepted dashboard handoffs are actively reinforcing this recommendation.")
|
||||
: TEXT("")
|
||||
);
|
||||
}
|
||||
RecordCoachHandoffHistorySnapshot(
|
||||
HyperTwistCoachDashboardWidgetInternal::DescribeHandoffRecommendationKind(
|
||||
HandoffRecommendationKind),
|
||||
CoachHandoffRecommendationStatusLine,
|
||||
CoachHandoffRecommendationDetailLine,
|
||||
CachedCoachPanelState.NextQueueSourceLabel,
|
||||
HandoffClosureBias.ContinuationBias,
|
||||
HandoffClosureBias.SuppressionBias,
|
||||
bQueueReadyForHandoff,
|
||||
bFollowUpReadyForHandoff,
|
||||
bRecommendedReadyForHandoff
|
||||
);
|
||||
FString QueuedRunLabel = CachedCoachPanelState.NextQueueSourceLabel.IsEmpty()
|
||||
? TEXT("Start Queue")
|
||||
: FString::Printf(TEXT("Queue: %s"), *CachedCoachPanelState.NextQueueSourceLabel);
|
||||
|
|
@ -4501,6 +4746,55 @@ void UHyperTwistCoachDashboardWidget::UpdateDashboardPresentation()
|
|||
default:
|
||||
break;
|
||||
}
|
||||
const FHyperTwistCoachDashboardHandoffHistoryEntry* SelectedHandoffHistoryEntry =
|
||||
SelectedHandoffHistoryIndex >= 0
|
||||
&& SelectedHandoffHistoryIndex < HandoffHistoryEntries.Num()
|
||||
? &HandoffHistoryEntries[SelectedHandoffHistoryIndex]
|
||||
: nullptr;
|
||||
const FString HandoffHistoryStatusLine =
|
||||
SelectedHandoffHistoryEntry != nullptr
|
||||
? FString::Printf(
|
||||
TEXT("Handoff history: %d entries | selected %d/%d | Recommendation: %s | Recorded: %s"),
|
||||
HandoffHistoryEntries.Num(),
|
||||
SelectedHandoffHistoryIndex + 1,
|
||||
HandoffHistoryEntries.Num(),
|
||||
SelectedHandoffHistoryEntry->RecommendedHandoffKindLabel.IsEmpty()
|
||||
? TEXT("none")
|
||||
: *SelectedHandoffHistoryEntry->RecommendedHandoffKindLabel,
|
||||
*SelectedHandoffHistoryEntry->RecordedAtUtc
|
||||
)
|
||||
: TEXT("Handoff history: no retained handoff-pattern shifts recorded yet.");
|
||||
const FString HandoffHistoryDetailLine =
|
||||
SelectedHandoffHistoryEntry != nullptr
|
||||
? FString::Printf(
|
||||
TEXT("Accepted queued/follow-up/recommended: %d/%d/%d | queue source: %s | preferred lane: %s | memory source: %s | recovery action: %s | recovery source: %s | closure continuation bias %d | closure suppression bias %d | readiness queue/follow-up/recommended: %s/%s/%s || %s"),
|
||||
SelectedHandoffHistoryEntry->AcceptedQueuedHandoffCount,
|
||||
SelectedHandoffHistoryEntry->AcceptedFollowUpHandoffCount,
|
||||
SelectedHandoffHistoryEntry->AcceptedRecommendedHandoffCount,
|
||||
SelectedHandoffHistoryEntry->QueueSourceLabel.IsEmpty()
|
||||
? TEXT("n/a")
|
||||
: *SelectedHandoffHistoryEntry->QueueSourceLabel,
|
||||
*HyperTwistCoachDashboardWidgetInternal::DescribeGuidanceLane(
|
||||
SelectedHandoffHistoryEntry->PreferredGuidanceLane),
|
||||
*HyperTwistCoachDashboardWidgetInternal::DescribeCoachMemorySourceLabel(
|
||||
SelectedHandoffHistoryEntry->PreferredGuidanceSourceLabel),
|
||||
SelectedHandoffHistoryEntry->PreferredRecoveryAction
|
||||
== EHyperTwistTrainingQueueRecoveryAction::None
|
||||
? TEXT("none")
|
||||
: *HyperTwistCoachDashboardWidgetInternal::EnumDisplayName(
|
||||
SelectedHandoffHistoryEntry->PreferredRecoveryAction),
|
||||
*HyperTwistCoachDashboardWidgetInternal::DescribeCoachMemorySourceLabel(
|
||||
SelectedHandoffHistoryEntry->PreferredRecoveryActionSourceLabel),
|
||||
SelectedHandoffHistoryEntry->ClosureContinuationBias,
|
||||
SelectedHandoffHistoryEntry->ClosureSuppressionBias,
|
||||
SelectedHandoffHistoryEntry->bQueueReady ? TEXT("yes") : TEXT("no"),
|
||||
SelectedHandoffHistoryEntry->bFollowUpReady ? TEXT("yes") : TEXT("no"),
|
||||
SelectedHandoffHistoryEntry->bRecommendedReady ? TEXT("yes") : TEXT("no"),
|
||||
SelectedHandoffHistoryEntry->RecommendationDetailLine.IsEmpty()
|
||||
? *SelectedHandoffHistoryEntry->RecommendationStatusLine
|
||||
: *SelectedHandoffHistoryEntry->RecommendationDetailLine
|
||||
)
|
||||
: TEXT("Handoff history detail: retained recommendation shifts and accepted queued/follow-up/recommended handoff patterns will accumulate here over time.");
|
||||
const auto BuildDeckPreviewLine = [](const FHyperTwistTrainingDeck& Deck, const FHyperTwistCoachBrief& Brief, const TCHAR* EmptyPrefix)
|
||||
{
|
||||
if (Deck.IsStructurallyValid())
|
||||
|
|
@ -5422,6 +5716,34 @@ void UHyperTwistCoachDashboardWidget::UpdateDashboardPresentation()
|
|||
{
|
||||
CoachHandoffRecommendationDetailTextBlock->SetText(FText::FromString(CoachHandoffRecommendationDetailLine));
|
||||
}
|
||||
if (HandoffHistoryHeaderTextBlock != nullptr)
|
||||
{
|
||||
HandoffHistoryHeaderTextBlock->SetText(FText::FromString(TEXT("[Handoff History]")));
|
||||
}
|
||||
if (HandoffHistoryStatusTextBlock != nullptr)
|
||||
{
|
||||
HandoffHistoryStatusTextBlock->SetText(FText::FromString(HandoffHistoryStatusLine));
|
||||
}
|
||||
if (HandoffHistoryDetailTextBlock != nullptr)
|
||||
{
|
||||
HandoffHistoryDetailTextBlock->SetText(FText::FromString(HandoffHistoryDetailLine));
|
||||
}
|
||||
if (PreviousHandoffHistoryButtonLabel != nullptr)
|
||||
{
|
||||
PreviousHandoffHistoryButtonLabel->SetText(FText::FromString(TEXT("Prev Handoff")));
|
||||
}
|
||||
if (PreviousHandoffHistoryButton != nullptr)
|
||||
{
|
||||
PreviousHandoffHistoryButton->SetIsEnabled(HandoffHistoryEntries.Num() > 1);
|
||||
}
|
||||
if (NextHandoffHistoryButtonLabel != nullptr)
|
||||
{
|
||||
NextHandoffHistoryButtonLabel->SetText(FText::FromString(TEXT("Next Handoff")));
|
||||
}
|
||||
if (NextHandoffHistoryButton != nullptr)
|
||||
{
|
||||
NextHandoffHistoryButton->SetIsEnabled(HandoffHistoryEntries.Num() > 1);
|
||||
}
|
||||
if (GuidanceReviewHeaderTextBlock != nullptr)
|
||||
{
|
||||
GuidanceReviewHeaderTextBlock->SetText(FText::FromString(TEXT("[Guidance Review]")));
|
||||
|
|
@ -5460,7 +5782,7 @@ void UHyperTwistCoachDashboardWidget::UpdateDashboardPresentation()
|
|||
}
|
||||
if (StartQueuedRunButton != nullptr)
|
||||
{
|
||||
StartQueuedRunButton->SetIsEnabled(bCanUseCoachHandoff && CachedCoachPanelState.bCanStartQueuedRun);
|
||||
StartQueuedRunButton->SetIsEnabled(bCanUseCoachHandoff && bQueueReadyForHandoff);
|
||||
}
|
||||
if (StartFollowUpRunButtonLabel != nullptr)
|
||||
{
|
||||
|
|
@ -5468,11 +5790,7 @@ void UHyperTwistCoachDashboardWidget::UpdateDashboardPresentation()
|
|||
}
|
||||
if (StartFollowUpRunButton != nullptr)
|
||||
{
|
||||
StartFollowUpRunButton->SetIsEnabled(
|
||||
bCanUseCoachHandoff
|
||||
&& CachedCoachPanelState.bCanStartCoachFollowUpRun
|
||||
&& CachedCoachPanelState.bHasFollowUpGuidance
|
||||
);
|
||||
StartFollowUpRunButton->SetIsEnabled(bCanUseCoachHandoff && bFollowUpReadyForHandoff);
|
||||
}
|
||||
if (StartRecommendedRunButtonLabel != nullptr)
|
||||
{
|
||||
|
|
@ -5480,9 +5798,7 @@ void UHyperTwistCoachDashboardWidget::UpdateDashboardPresentation()
|
|||
}
|
||||
if (StartRecommendedRunButton != nullptr)
|
||||
{
|
||||
StartRecommendedRunButton->SetIsEnabled(
|
||||
bCanUseCoachHandoff && CachedCoachPanelState.bCanStartCoachRecommendedRun
|
||||
);
|
||||
StartRecommendedRunButton->SetIsEnabled(bCanUseCoachHandoff && bRecommendedReadyForHandoff);
|
||||
}
|
||||
if (PreviewRecommendedButtonLabel != nullptr)
|
||||
{
|
||||
|
|
|
|||
|
|
@ -214,6 +214,36 @@ struct FHyperTwistCoachDashboardClosureRecoveryEntry
|
|||
}
|
||||
};
|
||||
|
||||
struct FHyperTwistCoachDashboardHandoffHistoryEntry
|
||||
{
|
||||
FString RecordedAtUtc;
|
||||
FString RecommendedHandoffKindLabel;
|
||||
FString RecommendationStatusLine;
|
||||
FString RecommendationDetailLine;
|
||||
FString QueueSourceLabel;
|
||||
EHyperTwistTrainingCoachGuidanceLane PreferredGuidanceLane =
|
||||
EHyperTwistTrainingCoachGuidanceLane::None;
|
||||
FString PreferredGuidanceSourceLabel;
|
||||
EHyperTwistTrainingQueueRecoveryAction PreferredRecoveryAction =
|
||||
EHyperTwistTrainingQueueRecoveryAction::None;
|
||||
FString PreferredRecoveryActionSourceLabel;
|
||||
int32 AcceptedQueuedHandoffCount = 0;
|
||||
int32 AcceptedFollowUpHandoffCount = 0;
|
||||
int32 AcceptedRecommendedHandoffCount = 0;
|
||||
int32 ClosureContinuationBias = 0;
|
||||
int32 ClosureSuppressionBias = 0;
|
||||
bool bQueueReady = false;
|
||||
bool bFollowUpReady = false;
|
||||
bool bRecommendedReady = false;
|
||||
|
||||
bool IsStructurallyValid() const
|
||||
{
|
||||
return !RecordedAtUtc.IsEmpty()
|
||||
&& !RecommendedHandoffKindLabel.IsEmpty()
|
||||
&& !RecommendationStatusLine.IsEmpty();
|
||||
}
|
||||
};
|
||||
|
||||
UCLASS(BlueprintType, Blueprintable)
|
||||
class UNREALHYPERTWIST_API UHyperTwistCoachDashboardWidget : public UHyperTwistTrainingPanelWidget
|
||||
{
|
||||
|
|
@ -256,6 +286,9 @@ public:
|
|||
UPROPERTY(VisibleAnywhere, BlueprintReadOnly, Category = "HyperTwist|Training|Coach|Dashboard")
|
||||
int32 SelectedGuidanceDecisionIndex = INDEX_NONE;
|
||||
|
||||
UPROPERTY(VisibleAnywhere, BlueprintReadOnly, Category = "HyperTwist|Training|Coach|Dashboard")
|
||||
int32 SelectedHandoffHistoryIndex = INDEX_NONE;
|
||||
|
||||
UPROPERTY(VisibleAnywhere, BlueprintReadOnly, Category = "HyperTwist|Training|Coach|Dashboard")
|
||||
int32 SelectedQueueSuppressionIndex = INDEX_NONE;
|
||||
|
||||
|
|
@ -349,6 +382,12 @@ public:
|
|||
UFUNCTION(BlueprintCallable, Category = "HyperTwist|Training|Coach|Dashboard")
|
||||
bool SelectNextGuidanceDecisionHistoryEntry();
|
||||
|
||||
UFUNCTION(BlueprintCallable, Category = "HyperTwist|Training|Coach|Dashboard")
|
||||
bool SelectPreviousHandoffHistoryEntry();
|
||||
|
||||
UFUNCTION(BlueprintCallable, Category = "HyperTwist|Training|Coach|Dashboard")
|
||||
bool SelectNextHandoffHistoryEntry();
|
||||
|
||||
UFUNCTION(BlueprintCallable, Category = "HyperTwist|Training|Coach|Dashboard")
|
||||
bool SelectPreviousQueueSuppressionHistoryEntry();
|
||||
|
||||
|
|
@ -490,6 +529,27 @@ protected:
|
|||
UPROPERTY(Transient)
|
||||
TObjectPtr<UTextBlock> CoachHandoffRecommendationDetailTextBlock = nullptr;
|
||||
|
||||
UPROPERTY(Transient)
|
||||
TObjectPtr<UTextBlock> HandoffHistoryHeaderTextBlock = nullptr;
|
||||
|
||||
UPROPERTY(Transient)
|
||||
TObjectPtr<UTextBlock> HandoffHistoryStatusTextBlock = nullptr;
|
||||
|
||||
UPROPERTY(Transient)
|
||||
TObjectPtr<UTextBlock> HandoffHistoryDetailTextBlock = nullptr;
|
||||
|
||||
UPROPERTY(Transient)
|
||||
TObjectPtr<UButton> PreviousHandoffHistoryButton = nullptr;
|
||||
|
||||
UPROPERTY(Transient)
|
||||
TObjectPtr<UTextBlock> PreviousHandoffHistoryButtonLabel = nullptr;
|
||||
|
||||
UPROPERTY(Transient)
|
||||
TObjectPtr<UButton> NextHandoffHistoryButton = nullptr;
|
||||
|
||||
UPROPERTY(Transient)
|
||||
TObjectPtr<UTextBlock> NextHandoffHistoryButtonLabel = nullptr;
|
||||
|
||||
UPROPERTY(Transient)
|
||||
TObjectPtr<UTextBlock> GuidanceReviewHeaderTextBlock = nullptr;
|
||||
|
||||
|
|
@ -931,6 +991,12 @@ protected:
|
|||
UFUNCTION()
|
||||
void HandleNextGuidanceDecisionClicked();
|
||||
|
||||
UFUNCTION()
|
||||
void HandlePreviousHandoffHistoryClicked();
|
||||
|
||||
UFUNCTION()
|
||||
void HandleNextHandoffHistoryClicked();
|
||||
|
||||
UFUNCTION()
|
||||
void HandlePreviousQueueSuppressionClicked();
|
||||
|
||||
|
|
@ -998,6 +1064,7 @@ private:
|
|||
void SyncRetainedRunRecap();
|
||||
void SyncSelectedGuidancePreviewCase();
|
||||
void SyncSelectedGuidanceDecisionHistoryEntry();
|
||||
void SyncSelectedHandoffHistoryEntry();
|
||||
void SyncSelectedQueueSuppressionHistoryEntry();
|
||||
void SyncSelectedClosureRecoveryHistoryEntry();
|
||||
void SyncPreferredGuidancePreviewLane(bool bForceAdoptPreferredLane);
|
||||
|
|
@ -1039,6 +1106,17 @@ private:
|
|||
bool bBeforeCompletionGap,
|
||||
bool bSucceeded
|
||||
);
|
||||
void RecordCoachHandoffHistorySnapshot(
|
||||
const FString& RecommendedHandoffKindLabel,
|
||||
const FString& RecommendationStatusLine,
|
||||
const FString& RecommendationDetailLine,
|
||||
const FString& QueueSourceLabel,
|
||||
int32 ClosureContinuationBias,
|
||||
int32 ClosureSuppressionBias,
|
||||
bool bQueueReady,
|
||||
bool bFollowUpReady,
|
||||
bool bRecommendedReady
|
||||
);
|
||||
void RecordQueueSuppressionSnapshot();
|
||||
void RecordClosureRecoverySnapshot();
|
||||
void RecordGuidanceOutcomeDecision(
|
||||
|
|
@ -1087,6 +1165,7 @@ private:
|
|||
FHyperTwistTrainingDeck RetainedGuidanceComparisonDeck;
|
||||
FString RetainedGuidanceComparisonSelectedCaseId;
|
||||
TArray<FHyperTwistCoachDashboardDecisionHistoryEntry> GuidanceDecisionHistoryEntries;
|
||||
TArray<FHyperTwistCoachDashboardHandoffHistoryEntry> HandoffHistoryEntries;
|
||||
bool bHasQueueRecoveryOutcome = false;
|
||||
FHyperTwistCoachDashboardQueueRecoveryOutcome QueueRecoveryOutcome;
|
||||
TArray<FHyperTwistCoachDashboardQueueRecoveryOutcome> QueueRecoveryOutcomeHistory;
|
||||
|
|
|
|||
Loading…
Add table
Reference in a new issue