Weight dashboard guidance by decision history
This commit is contained in:
parent
214aeb42b7
commit
059d34d98a
2 changed files with 234 additions and 1 deletions
|
|
@ -1290,6 +1290,24 @@ void UHyperTwistCoachDashboardWidget::EnsureDefaultDashboardBuilt()
|
|||
TEXT("CoachGuidanceOutcomeDetail"),
|
||||
8
|
||||
);
|
||||
GuidanceDecisionWeightingHeaderTextBlock = HyperTwistCoachDashboardWidgetInternal::AddTextRow(
|
||||
WidgetTree,
|
||||
RootLayout,
|
||||
TEXT("CoachGuidanceDecisionWeightingHeader"),
|
||||
2
|
||||
);
|
||||
GuidanceDecisionWeightingStatusTextBlock = HyperTwistCoachDashboardWidgetInternal::AddTextRow(
|
||||
WidgetTree,
|
||||
RootLayout,
|
||||
TEXT("CoachGuidanceDecisionWeightingStatus"),
|
||||
2
|
||||
);
|
||||
GuidanceDecisionWeightingDetailTextBlock = HyperTwistCoachDashboardWidgetInternal::AddTextRow(
|
||||
WidgetTree,
|
||||
RootLayout,
|
||||
TEXT("CoachGuidanceDecisionWeightingDetail"),
|
||||
8
|
||||
);
|
||||
GuidanceDecisionHistoryHeaderTextBlock = HyperTwistCoachDashboardWidgetInternal::AddTextRow(
|
||||
WidgetTree,
|
||||
RootLayout,
|
||||
|
|
@ -2516,6 +2534,143 @@ EHyperTwistCoachDashboardOutcomeAction UHyperTwistCoachDashboardWidget::DeriveGu
|
|||
: EHyperTwistCoachDashboardOutcomeAction::RedirectCoachGuidance;
|
||||
}
|
||||
|
||||
FHyperTwistCoachDashboardDecisionWeighting UHyperTwistCoachDashboardWidget::BuildGuidanceDecisionWeighting(
|
||||
const EHyperTwistCoachDashboardOutcomeAction BaseAction
|
||||
) const
|
||||
{
|
||||
FHyperTwistCoachDashboardDecisionWeighting Weighting;
|
||||
Weighting.BaseAction = BaseAction;
|
||||
Weighting.WeightedAction = BaseAction;
|
||||
if (BaseAction == EHyperTwistCoachDashboardOutcomeAction::None)
|
||||
{
|
||||
return Weighting;
|
||||
}
|
||||
|
||||
const FHyperTwistTrainingSessionSummary* DisplayedRunRecap = nullptr;
|
||||
const FHyperTwistCoachBrief* DisplayedGuidanceComparisonBrief = nullptr;
|
||||
const FHyperTwistTrainingDeck* DisplayedGuidanceComparisonDeck = nullptr;
|
||||
FString DisplayedGuidanceComparisonSelectedCaseId;
|
||||
bool bDisplayedGuidanceComparisonSingleCase = false;
|
||||
if (!ResolveDisplayedGuidanceComparisonContext(
|
||||
DisplayedRunRecap,
|
||||
DisplayedGuidanceComparisonBrief,
|
||||
DisplayedGuidanceComparisonDeck,
|
||||
DisplayedGuidanceComparisonSelectedCaseId,
|
||||
bDisplayedGuidanceComparisonSingleCase)
|
||||
|| DisplayedRunRecap == nullptr
|
||||
|| DisplayedGuidanceComparisonDeck == nullptr)
|
||||
{
|
||||
return Weighting;
|
||||
}
|
||||
|
||||
EHyperTwistCoachDashboardGuidancePreviewLane DisplayedGuidanceComparisonLane =
|
||||
EHyperTwistCoachDashboardGuidancePreviewLane::None;
|
||||
if (!ActiveGuidanceLaunchSessionId.IsEmpty()
|
||||
&& DisplayedRunRecap->TrainingSessionId == ActiveGuidanceLaunchSessionId)
|
||||
{
|
||||
DisplayedGuidanceComparisonLane = ActiveGuidanceLaunchLane;
|
||||
}
|
||||
else if (!RetainedGuidanceComparisonSessionId.IsEmpty()
|
||||
&& DisplayedRunRecap->TrainingSessionId == RetainedGuidanceComparisonSessionId)
|
||||
{
|
||||
DisplayedGuidanceComparisonLane = RetainedGuidanceComparisonLane;
|
||||
}
|
||||
|
||||
if (DisplayedGuidanceComparisonLane == EHyperTwistCoachDashboardGuidancePreviewLane::None)
|
||||
{
|
||||
return Weighting;
|
||||
}
|
||||
|
||||
constexpr int32 MaxMatchedDecisionEntries = 6;
|
||||
for (int32 Index = GuidanceDecisionHistoryEntries.Num() - 1;
|
||||
Index >= 0 && Weighting.MatchingDecisionCount < MaxMatchedDecisionEntries;
|
||||
--Index)
|
||||
{
|
||||
const FHyperTwistCoachDashboardDecisionHistoryEntry& Entry = GuidanceDecisionHistoryEntries[Index];
|
||||
if (Entry.GuidanceLane != DisplayedGuidanceComparisonLane
|
||||
|| Entry.bSingleCaseScope != bDisplayedGuidanceComparisonSingleCase)
|
||||
{
|
||||
continue;
|
||||
}
|
||||
|
||||
if (bDisplayedGuidanceComparisonSingleCase
|
||||
&& !DisplayedGuidanceComparisonSelectedCaseId.IsEmpty()
|
||||
&& Entry.SelectedCaseId != DisplayedGuidanceComparisonSelectedCaseId)
|
||||
{
|
||||
continue;
|
||||
}
|
||||
|
||||
++Weighting.MatchingDecisionCount;
|
||||
Weighting.AverageSuccessRate += Entry.SuccessRate;
|
||||
if (Weighting.LastAppliedAction == EHyperTwistCoachDashboardOutcomeAction::None)
|
||||
{
|
||||
Weighting.LastAppliedAction = Entry.AppliedAction;
|
||||
}
|
||||
|
||||
switch (Entry.AppliedAction)
|
||||
{
|
||||
case EHyperTwistCoachDashboardOutcomeAction::RepeatSelectedCase:
|
||||
++Weighting.RepeatSelectedCaseCount;
|
||||
break;
|
||||
case EHyperTwistCoachDashboardOutcomeAction::RepeatPreviewDeck:
|
||||
++Weighting.RepeatPreviewDeckCount;
|
||||
break;
|
||||
case EHyperTwistCoachDashboardOutcomeAction::BroadenPreviewDeck:
|
||||
++Weighting.BroadenPreviewDeckCount;
|
||||
break;
|
||||
case EHyperTwistCoachDashboardOutcomeAction::RedirectCoachGuidance:
|
||||
++Weighting.RedirectCoachGuidanceCount;
|
||||
break;
|
||||
default:
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
if (Weighting.MatchingDecisionCount <= 0)
|
||||
{
|
||||
return Weighting;
|
||||
}
|
||||
|
||||
Weighting.AverageSuccessRate /= static_cast<float>(Weighting.MatchingDecisionCount);
|
||||
|
||||
if (bDisplayedGuidanceComparisonSingleCase)
|
||||
{
|
||||
if (BaseAction == EHyperTwistCoachDashboardOutcomeAction::BroadenPreviewDeck
|
||||
&& Weighting.RepeatSelectedCaseCount >= 2
|
||||
&& Weighting.AverageSuccessRate < 0.90f)
|
||||
{
|
||||
Weighting.WeightedAction = EHyperTwistCoachDashboardOutcomeAction::RepeatSelectedCase;
|
||||
Weighting.bHasOverride = true;
|
||||
}
|
||||
else if (BaseAction == EHyperTwistCoachDashboardOutcomeAction::RepeatSelectedCase
|
||||
&& Weighting.BroadenPreviewDeckCount >= 2
|
||||
&& Weighting.AverageSuccessRate >= 0.78f)
|
||||
{
|
||||
Weighting.WeightedAction = EHyperTwistCoachDashboardOutcomeAction::BroadenPreviewDeck;
|
||||
Weighting.bHasOverride = true;
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
if (BaseAction == EHyperTwistCoachDashboardOutcomeAction::RedirectCoachGuidance
|
||||
&& Weighting.RepeatPreviewDeckCount >= 2
|
||||
&& Weighting.AverageSuccessRate < 0.80f)
|
||||
{
|
||||
Weighting.WeightedAction = EHyperTwistCoachDashboardOutcomeAction::RepeatPreviewDeck;
|
||||
Weighting.bHasOverride = true;
|
||||
}
|
||||
else if (BaseAction == EHyperTwistCoachDashboardOutcomeAction::RepeatPreviewDeck
|
||||
&& Weighting.RedirectCoachGuidanceCount >= 2
|
||||
&& Weighting.AverageSuccessRate >= 0.72f)
|
||||
{
|
||||
Weighting.WeightedAction = EHyperTwistCoachDashboardOutcomeAction::RedirectCoachGuidance;
|
||||
Weighting.bHasOverride = true;
|
||||
}
|
||||
}
|
||||
|
||||
return Weighting;
|
||||
}
|
||||
|
||||
FString UHyperTwistCoachDashboardWidget::BuildGuidanceOutcomeActionLabel(
|
||||
const EHyperTwistCoachDashboardOutcomeAction Action) const
|
||||
{
|
||||
|
|
@ -3239,7 +3394,13 @@ void UHyperTwistCoachDashboardWidget::UpdateDashboardPresentation()
|
|||
);
|
||||
}
|
||||
}
|
||||
const EHyperTwistCoachDashboardOutcomeAction GuidanceOutcomeAction = DeriveGuidanceOutcomeAction();
|
||||
const EHyperTwistCoachDashboardOutcomeAction BaseGuidanceOutcomeAction = DeriveGuidanceOutcomeAction();
|
||||
const FHyperTwistCoachDashboardDecisionWeighting GuidanceDecisionWeighting =
|
||||
BuildGuidanceDecisionWeighting(BaseGuidanceOutcomeAction);
|
||||
const EHyperTwistCoachDashboardOutcomeAction GuidanceOutcomeAction =
|
||||
GuidanceDecisionWeighting.WeightedAction != EHyperTwistCoachDashboardOutcomeAction::None
|
||||
? GuidanceDecisionWeighting.WeightedAction
|
||||
: BaseGuidanceOutcomeAction;
|
||||
const bool bCanRedirectGuidanceOutcome =
|
||||
CachedCoachPanelState.bCanStartCoachFollowUpRun
|
||||
|| CachedCoachPanelState.bCanStartQueuedRun
|
||||
|
|
@ -3253,6 +3414,30 @@ void UHyperTwistCoachDashboardWidget::UpdateDashboardPresentation()
|
|||
&& (GuidanceOutcomeAction != EHyperTwistCoachDashboardOutcomeAction::RedirectCoachGuidance
|
||||
|| bCanRedirectGuidanceOutcome);
|
||||
const FString GuidanceOutcomeActionLabel = BuildGuidanceOutcomeActionLabel(GuidanceOutcomeAction);
|
||||
const FString GuidanceDecisionWeightingStatusLine =
|
||||
GuidanceDecisionWeighting.IsStructurallyValid()
|
||||
? FString::Printf(
|
||||
TEXT("Weighting: %d matching decisions | Base: %s | Weighted: %s | Last applied: %s"),
|
||||
GuidanceDecisionWeighting.MatchingDecisionCount,
|
||||
*BuildGuidanceOutcomeActionLabel(GuidanceDecisionWeighting.BaseAction),
|
||||
*BuildGuidanceOutcomeActionLabel(GuidanceDecisionWeighting.WeightedAction),
|
||||
*BuildGuidanceOutcomeActionLabel(GuidanceDecisionWeighting.LastAppliedAction)
|
||||
)
|
||||
: TEXT("Weighting: no matching authored decision history yet; the current outcome action is recap-derived only.");
|
||||
const FString GuidanceDecisionWeightingDetailLine =
|
||||
GuidanceDecisionWeighting.IsStructurallyValid()
|
||||
? FString::Printf(
|
||||
TEXT("Decision weighting for this lane/scope: repeat case %d | repeat deck %d | broaden %d | redirect %d | Avg success %.2f | %s"),
|
||||
GuidanceDecisionWeighting.RepeatSelectedCaseCount,
|
||||
GuidanceDecisionWeighting.RepeatPreviewDeckCount,
|
||||
GuidanceDecisionWeighting.BroadenPreviewDeckCount,
|
||||
GuidanceDecisionWeighting.RedirectCoachGuidanceCount,
|
||||
GuidanceDecisionWeighting.AverageSuccessRate,
|
||||
GuidanceDecisionWeighting.bHasOverride
|
||||
? TEXT("Recent authored choices adjusted the current guidance-outcome action.")
|
||||
: TEXT("Recent authored choices reinforced the recap-derived guidance-outcome action.")
|
||||
)
|
||||
: TEXT("Weighting detail: apply outcome actions on the same guidance lane and scope to let the dashboard bias future recommendations.");
|
||||
const FHyperTwistCoachDashboardDecisionHistoryEntry* SelectedGuidanceDecisionEntry =
|
||||
SelectedGuidanceDecisionIndex >= 0
|
||||
&& SelectedGuidanceDecisionIndex < GuidanceDecisionHistoryEntries.Num()
|
||||
|
|
@ -3440,6 +3625,18 @@ void UHyperTwistCoachDashboardWidget::UpdateDashboardPresentation()
|
|||
{
|
||||
GuidanceOutcomeActionButton->SetIsEnabled(bCanApplyGuidanceOutcomeAction);
|
||||
}
|
||||
if (GuidanceDecisionWeightingHeaderTextBlock != nullptr)
|
||||
{
|
||||
GuidanceDecisionWeightingHeaderTextBlock->SetText(FText::FromString(TEXT("[Decision Weighting]")));
|
||||
}
|
||||
if (GuidanceDecisionWeightingStatusTextBlock != nullptr)
|
||||
{
|
||||
GuidanceDecisionWeightingStatusTextBlock->SetText(FText::FromString(GuidanceDecisionWeightingStatusLine));
|
||||
}
|
||||
if (GuidanceDecisionWeightingDetailTextBlock != nullptr)
|
||||
{
|
||||
GuidanceDecisionWeightingDetailTextBlock->SetText(FText::FromString(GuidanceDecisionWeightingDetailLine));
|
||||
}
|
||||
if (GuidanceDecisionHistoryHeaderTextBlock != nullptr)
|
||||
{
|
||||
GuidanceDecisionHistoryHeaderTextBlock->SetText(FText::FromString(TEXT("[Decision History]")));
|
||||
|
|
|
|||
|
|
@ -60,6 +60,30 @@ struct FHyperTwistCoachDashboardDecisionHistoryEntry
|
|||
}
|
||||
};
|
||||
|
||||
struct FHyperTwistCoachDashboardDecisionWeighting
|
||||
{
|
||||
int32 MatchingDecisionCount = 0;
|
||||
int32 RepeatSelectedCaseCount = 0;
|
||||
int32 RepeatPreviewDeckCount = 0;
|
||||
int32 BroadenPreviewDeckCount = 0;
|
||||
int32 RedirectCoachGuidanceCount = 0;
|
||||
EHyperTwistCoachDashboardOutcomeAction BaseAction =
|
||||
EHyperTwistCoachDashboardOutcomeAction::None;
|
||||
EHyperTwistCoachDashboardOutcomeAction WeightedAction =
|
||||
EHyperTwistCoachDashboardOutcomeAction::None;
|
||||
EHyperTwistCoachDashboardOutcomeAction LastAppliedAction =
|
||||
EHyperTwistCoachDashboardOutcomeAction::None;
|
||||
float AverageSuccessRate = 0.0f;
|
||||
bool bHasOverride = false;
|
||||
|
||||
bool IsStructurallyValid() const
|
||||
{
|
||||
return MatchingDecisionCount > 0
|
||||
&& BaseAction != EHyperTwistCoachDashboardOutcomeAction::None
|
||||
&& WeightedAction != EHyperTwistCoachDashboardOutcomeAction::None;
|
||||
}
|
||||
};
|
||||
|
||||
UCLASS(BlueprintType, Blueprintable)
|
||||
class UNREALHYPERTWIST_API UHyperTwistCoachDashboardWidget : public UHyperTwistTrainingPanelWidget
|
||||
{
|
||||
|
|
@ -261,6 +285,15 @@ protected:
|
|||
UPROPERTY(Transient)
|
||||
TObjectPtr<UTextBlock> GuidanceOutcomeActionButtonLabel = nullptr;
|
||||
|
||||
UPROPERTY(Transient)
|
||||
TObjectPtr<UTextBlock> GuidanceDecisionWeightingHeaderTextBlock = nullptr;
|
||||
|
||||
UPROPERTY(Transient)
|
||||
TObjectPtr<UTextBlock> GuidanceDecisionWeightingStatusTextBlock = nullptr;
|
||||
|
||||
UPROPERTY(Transient)
|
||||
TObjectPtr<UTextBlock> GuidanceDecisionWeightingDetailTextBlock = nullptr;
|
||||
|
||||
UPROPERTY(Transient)
|
||||
TObjectPtr<UTextBlock> GuidanceDecisionHistoryHeaderTextBlock = nullptr;
|
||||
|
||||
|
|
@ -726,6 +759,9 @@ private:
|
|||
bool& bOutDisplayedGuidanceComparisonSingleCase
|
||||
) const;
|
||||
EHyperTwistCoachDashboardOutcomeAction DeriveGuidanceOutcomeAction() const;
|
||||
FHyperTwistCoachDashboardDecisionWeighting BuildGuidanceDecisionWeighting(
|
||||
EHyperTwistCoachDashboardOutcomeAction BaseAction
|
||||
) const;
|
||||
FString BuildGuidanceOutcomeActionLabel(EHyperTwistCoachDashboardOutcomeAction Action) const;
|
||||
FHyperTwistTrainingDeck BuildGuidanceOutcomeSelectedCaseDeck() const;
|
||||
void RecordGuidanceOutcomeDecision(
|
||||
|
|
|
|||
Loading…
Add table
Reference in a new issue