Add coach panel projection and queued run bootstrap
This commit is contained in:
parent
bbdee1398e
commit
efe45636ec
9 changed files with 590 additions and 0 deletions
|
|
@ -29,6 +29,7 @@ void UHyperTwistTrainingPanelWidget::RefreshTrainingState()
|
|||
UHyperTwistTrainingRuntimeLibrary::GetActiveTrainingCoachSchedulePolicySummary(this);
|
||||
CachedCoachQueueExecutionSummary =
|
||||
UHyperTwistTrainingRuntimeLibrary::GetActiveTrainingCoachSessionQueueExecutionSummary(this);
|
||||
CachedCoachPanelState = UHyperTwistTrainingRuntimeLibrary::GetActiveTrainingCoachPanelState(this);
|
||||
CachedRepositoryRoundTripVerification =
|
||||
UHyperTwistTrainingRuntimeLibrary::GetActiveTrainingRepositoryRoundTripVerification(this);
|
||||
}
|
||||
|
|
@ -132,6 +133,17 @@ FHyperTwistTrainingRunState UHyperTwistTrainingPanelWidget::StartCoachFollowUpRu
|
|||
return CachedRunState;
|
||||
}
|
||||
|
||||
FHyperTwistTrainingRunState UHyperTwistTrainingPanelWidget::StartNextQueuedCoachRun(const int32 MaxCases)
|
||||
{
|
||||
CachedRunState = UHyperTwistTrainingRuntimeLibrary::StartNextQueuedCoachRun(
|
||||
this,
|
||||
DefaultSessionId + TEXT("_queue"),
|
||||
MaxCases > 0 ? MaxCases : DefaultCoachMaxCases
|
||||
);
|
||||
RefreshTrainingState();
|
||||
return CachedRunState;
|
||||
}
|
||||
|
||||
FHyperTwistTrainingRepositoryRoundTripVerification
|
||||
UHyperTwistTrainingPanelWidget::VerifyTrainingRepositoryRoundTrip()
|
||||
{
|
||||
|
|
|
|||
|
|
@ -127,6 +127,18 @@ UHyperTwistTrainingRuntimeLibrary::GetActiveTrainingRepositoryRoundTripVerificat
|
|||
return FHyperTwistTrainingRepositoryRoundTripVerification();
|
||||
}
|
||||
|
||||
FHyperTwistTrainingCoachPanelState UHyperTwistTrainingRuntimeLibrary::GetActiveTrainingCoachPanelState(
|
||||
UObject* WorldContextObject
|
||||
)
|
||||
{
|
||||
if (UHyperTwistTrainingSubsystem* TrainingSubsystem = GetTrainingSubsystem(WorldContextObject))
|
||||
{
|
||||
return TrainingSubsystem->GetActiveCoachPanelState();
|
||||
}
|
||||
|
||||
return FHyperTwistTrainingCoachPanelState();
|
||||
}
|
||||
|
||||
FHyperTwistTrainingDeckHistorySummary UHyperTwistTrainingRuntimeLibrary::GetActiveTrainingDeckHistorySummary(UObject* WorldContextObject)
|
||||
{
|
||||
if (UHyperTwistTrainingSubsystem* TrainingSubsystem = GetTrainingSubsystem(WorldContextObject))
|
||||
|
|
@ -1017,6 +1029,20 @@ FHyperTwistTrainingRunState UHyperTwistTrainingRuntimeLibrary::StartCoachFollowU
|
|||
return FHyperTwistTrainingRunState();
|
||||
}
|
||||
|
||||
FHyperTwistTrainingRunState UHyperTwistTrainingRuntimeLibrary::StartNextQueuedCoachRun(
|
||||
UObject* WorldContextObject,
|
||||
const FString& SessionId,
|
||||
int32 MaxCases
|
||||
)
|
||||
{
|
||||
if (UHyperTwistTrainingSubsystem* TrainingSubsystem = GetTrainingSubsystem(WorldContextObject))
|
||||
{
|
||||
return TrainingSubsystem->StartNextQueuedCoachRun(SessionId, MaxCases);
|
||||
}
|
||||
|
||||
return FHyperTwistTrainingRunState();
|
||||
}
|
||||
|
||||
FHyperTwistTrainingRunState UHyperTwistTrainingRuntimeLibrary::StartSampleClassicTrainingRun(
|
||||
UObject* WorldContextObject,
|
||||
const FString& UserId,
|
||||
|
|
|
|||
|
|
@ -92,6 +92,7 @@ void AHyperTwistTrainingSessionActor::RefreshCachedTrainingState()
|
|||
UHyperTwistTrainingRuntimeLibrary::GetActiveTrainingCoachSchedulePolicySummary(this);
|
||||
CachedCoachQueueExecutionSummary =
|
||||
UHyperTwistTrainingRuntimeLibrary::GetActiveTrainingCoachSessionQueueExecutionSummary(this);
|
||||
CachedCoachPanelState = UHyperTwistTrainingRuntimeLibrary::GetActiveTrainingCoachPanelState(this);
|
||||
CachedRepositoryRoundTripVerification =
|
||||
UHyperTwistTrainingRuntimeLibrary::GetActiveTrainingRepositoryRoundTripVerification(this);
|
||||
}
|
||||
|
|
@ -142,6 +143,19 @@ FHyperTwistTrainingRunState AHyperTwistTrainingSessionActor::StartCoachFollowUpT
|
|||
return CachedRunState;
|
||||
}
|
||||
|
||||
FHyperTwistTrainingRunState AHyperTwistTrainingSessionActor::StartNextQueuedCoachTrainingRun(
|
||||
const int32 MaxCases
|
||||
)
|
||||
{
|
||||
CachedRunState = UHyperTwistTrainingRuntimeLibrary::StartNextQueuedCoachRun(
|
||||
this,
|
||||
AutoStartSessionId + TEXT("_queue"),
|
||||
MaxCases > 0 ? MaxCases : DefaultCoachMaxCases
|
||||
);
|
||||
RefreshCachedTrainingState();
|
||||
return CachedRunState;
|
||||
}
|
||||
|
||||
FHyperTwistTrainingRepositoryRoundTripVerification
|
||||
AHyperTwistTrainingSessionActor::VerifyRepositoryRoundTrip()
|
||||
{
|
||||
|
|
|
|||
|
|
@ -11,6 +11,192 @@
|
|||
namespace HyperTwistTrainingSubsystemInternal
|
||||
{
|
||||
const TCHAR* ActiveCoachSessionQueueId = TEXT("coach_queue_active");
|
||||
|
||||
EHyperTwistCoachPriority CoachPriorityFromScore(const float Score)
|
||||
{
|
||||
if (Score >= 85.0f)
|
||||
{
|
||||
return EHyperTwistCoachPriority::Critical;
|
||||
}
|
||||
if (Score >= 65.0f)
|
||||
{
|
||||
return EHyperTwistCoachPriority::Elevated;
|
||||
}
|
||||
if (Score >= 40.0f)
|
||||
{
|
||||
return EHyperTwistCoachPriority::Advisory;
|
||||
}
|
||||
return EHyperTwistCoachPriority::Informational;
|
||||
}
|
||||
|
||||
FString ResolveQueueActionLabel(const FString& SourceLabel)
|
||||
{
|
||||
if (SourceLabel == TEXT("follow-up-brief"))
|
||||
{
|
||||
return TEXT("Start coach follow-up run");
|
||||
}
|
||||
if (SourceLabel == TEXT("archive-policy"))
|
||||
{
|
||||
return TEXT("Start archive retention run");
|
||||
}
|
||||
if (SourceLabel == TEXT("primary-brief"))
|
||||
{
|
||||
return TEXT("Start coach recommended run");
|
||||
}
|
||||
return TEXT("Start queued coach run");
|
||||
}
|
||||
|
||||
const FHyperTwistTrainingCoachSessionQueueStateEntry* FindNextRunnableCoachQueueEntry(
|
||||
const FHyperTwistTrainingCoachSessionQueueState& QueueState
|
||||
)
|
||||
{
|
||||
if (!QueueState.IsStructurallyValid())
|
||||
{
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
const FHyperTwistTrainingCoachSessionQueueStateEntry* BestEntry = nullptr;
|
||||
for (const FHyperTwistTrainingCoachSessionQueueStateEntry& Candidate : QueueState.Entries)
|
||||
{
|
||||
const bool bIsRunnable = Candidate.EntryState == EHyperTwistTrainingCoachSessionQueueEntryState::Ready
|
||||
|| (Candidate.EntryState == EHyperTwistTrainingCoachSessionQueueEntryState::Pending
|
||||
&& Candidate.bCanStartImmediately);
|
||||
if (!bIsRunnable)
|
||||
{
|
||||
continue;
|
||||
}
|
||||
|
||||
if (BestEntry == nullptr || Candidate.SlotIndex < BestEntry->SlotIndex)
|
||||
{
|
||||
BestEntry = &Candidate;
|
||||
}
|
||||
}
|
||||
|
||||
return BestEntry;
|
||||
}
|
||||
|
||||
FHyperTwistTrainingDeck BuildCoachQueueDeckFromEntry(
|
||||
const FHyperTwistTrainingCoachSessionQueueStateEntry& QueueEntry,
|
||||
const FHyperTwistTrainingRunState& ActiveRunState,
|
||||
int32 MaxCases
|
||||
)
|
||||
{
|
||||
const int32 DesiredCaseCount = MaxCases > 0 ? MaxCases : 3;
|
||||
FHyperTwistTrainingDeck SourceDeck;
|
||||
if (!QueueEntry.FocusDeckId.IsEmpty())
|
||||
{
|
||||
UHyperTwistTrainingCatalogLibrary::TryFindDeckInCatalog(
|
||||
UHyperTwistTrainingCatalogLibrary::MakePhase3TrainingCatalog(),
|
||||
QueueEntry.FocusDeckId,
|
||||
SourceDeck
|
||||
);
|
||||
}
|
||||
|
||||
if (!SourceDeck.IsStructurallyValid()
|
||||
&& ActiveRunState.ActiveDeck.IsStructurallyValid()
|
||||
&& (QueueEntry.FocusDeckId.IsEmpty()
|
||||
|| QueueEntry.FocusDeckId == ActiveRunState.ActiveDeck.DeckId))
|
||||
{
|
||||
SourceDeck = ActiveRunState.ActiveDeck;
|
||||
}
|
||||
|
||||
if (!SourceDeck.IsStructurallyValid())
|
||||
{
|
||||
return FHyperTwistTrainingDeck();
|
||||
}
|
||||
|
||||
TArray<FHyperTwistTrainingCase> SelectedCases;
|
||||
if (QueueEntry.FocusCaseIds.Num() > 0)
|
||||
{
|
||||
for (const FString& FocusCaseId : QueueEntry.FocusCaseIds)
|
||||
{
|
||||
const FHyperTwistTrainingCase* MatchingCase = SourceDeck.Cases.FindByPredicate(
|
||||
[&FocusCaseId](const FHyperTwistTrainingCase& Candidate)
|
||||
{
|
||||
return Candidate.CaseId == FocusCaseId;
|
||||
}
|
||||
);
|
||||
if (MatchingCase != nullptr)
|
||||
{
|
||||
SelectedCases.Add(*MatchingCase);
|
||||
if (SelectedCases.Num() >= DesiredCaseCount)
|
||||
{
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if (SelectedCases.Num() == 0)
|
||||
{
|
||||
for (const FHyperTwistTrainingCase& Candidate : SourceDeck.Cases)
|
||||
{
|
||||
SelectedCases.Add(Candidate);
|
||||
if (SelectedCases.Num() >= DesiredCaseCount)
|
||||
{
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if (SelectedCases.Num() == 0)
|
||||
{
|
||||
return FHyperTwistTrainingDeck();
|
||||
}
|
||||
|
||||
FHyperTwistTrainingDeck FocusDeck = SourceDeck;
|
||||
FocusDeck.Cases = MoveTemp(SelectedCases);
|
||||
FocusDeck.SelectionPolicy = QueueEntry.SuggestedSelectionPolicy;
|
||||
if (QueueEntry.SuggestedMode != EHyperTwistTrainingDeliveryMode::Timer
|
||||
&& !FocusDeck.DeliveryModes.Contains(QueueEntry.SuggestedMode))
|
||||
{
|
||||
FocusDeck.DeliveryModes.Insert(QueueEntry.SuggestedMode, 0);
|
||||
}
|
||||
if (!QueueEntry.Headline.IsEmpty())
|
||||
{
|
||||
FocusDeck.Title = FString::Printf(TEXT("%s - %s"), *SourceDeck.Title, *QueueEntry.Headline);
|
||||
}
|
||||
if (!FocusDeck.Tags.Contains(TEXT("coach-queue")))
|
||||
{
|
||||
FocusDeck.Tags.Add(TEXT("coach-queue"));
|
||||
}
|
||||
if (!QueueEntry.SourceLabel.IsEmpty())
|
||||
{
|
||||
FocusDeck.Tags.AddUnique(FString::Printf(TEXT("coach-queue:%s"), *QueueEntry.SourceLabel.ToLower()));
|
||||
}
|
||||
if (!QueueEntry.FocusMethodSegmentId.IsEmpty())
|
||||
{
|
||||
FocusDeck.SubsetId = QueueEntry.FocusMethodSegmentId;
|
||||
}
|
||||
return FocusDeck;
|
||||
}
|
||||
|
||||
FHyperTwistCoachBrief BuildCoachBriefFromQueueEntry(
|
||||
const FHyperTwistTrainingCoachSessionQueueState& QueueState,
|
||||
const FHyperTwistTrainingCoachSessionQueueStateEntry& QueueEntry,
|
||||
const FString& ReferenceUtc
|
||||
)
|
||||
{
|
||||
FHyperTwistCoachBrief Brief;
|
||||
Brief.UserId = QueueState.UserId;
|
||||
Brief.ReferenceUtc = ReferenceUtc;
|
||||
Brief.PriorityScore = QueueEntry.PriorityScore;
|
||||
Brief.Priority = CoachPriorityFromScore(QueueEntry.PriorityScore);
|
||||
Brief.FocusLane = QueueEntry.SourceLabel == TEXT("archive-policy")
|
||||
? EHyperTwistCoachFocusLane::Stability
|
||||
: EHyperTwistCoachFocusLane::ReviewBacklog;
|
||||
Brief.Headline = QueueEntry.Headline;
|
||||
Brief.PrimaryActionLabel = ResolveQueueActionLabel(QueueEntry.SourceLabel);
|
||||
Brief.SuggestedMode = QueueEntry.SuggestedMode;
|
||||
Brief.SuggestedSelectionPolicy = QueueEntry.SuggestedSelectionPolicy;
|
||||
Brief.FocusDeckId = QueueEntry.FocusDeckId;
|
||||
Brief.FocusMethodSegmentId = QueueEntry.FocusMethodSegmentId;
|
||||
Brief.FocusCaseIds = QueueEntry.FocusCaseIds;
|
||||
Brief.bNeedsCoachReview = QueueEntry.SuggestedMode == EHyperTwistTrainingDeliveryMode::CoachReviewed;
|
||||
Brief.bPreferRecognitionReplayReview =
|
||||
QueueEntry.SuggestedMode == EHyperTwistTrainingDeliveryMode::RecognitionAssisted;
|
||||
return Brief;
|
||||
}
|
||||
}
|
||||
|
||||
bool UHyperTwistTrainingSubsystem::HasActiveRun() const
|
||||
|
|
@ -59,6 +245,131 @@ UHyperTwistTrainingSubsystem::GetActiveRepositoryRoundTripVerification() const
|
|||
return ActiveRepositoryRoundTripVerification;
|
||||
}
|
||||
|
||||
FHyperTwistTrainingCoachPanelState UHyperTwistTrainingSubsystem::GetActiveCoachPanelState() const
|
||||
{
|
||||
FHyperTwistTrainingCoachPanelState PanelState;
|
||||
PanelState.UserId = !ActiveCoachBrief.UserId.IsEmpty()
|
||||
? ActiveCoachBrief.UserId
|
||||
: (!ActiveCoachSessionQueueSummary.UserId.IsEmpty()
|
||||
? ActiveCoachSessionQueueSummary.UserId
|
||||
: ActiveRunState.Session.UserId);
|
||||
PanelState.bHasActiveRun = HasActiveRun();
|
||||
PanelState.bHasCoachGuidance = !ActiveCoachBrief.UserId.IsEmpty() && !ActiveCoachBrief.Headline.IsEmpty();
|
||||
PanelState.bHasFollowUpGuidance =
|
||||
!ActiveCoachFollowUpBrief.UserId.IsEmpty() && !ActiveCoachFollowUpBrief.Headline.IsEmpty();
|
||||
PanelState.SignalCount = ActiveCoachSignals.Num();
|
||||
PanelState.bRepositoryVerificationPassed = ActiveRepositoryRoundTripVerification.bDidRoundTripPass;
|
||||
PanelState.bRepositoryVerificationNeedsAttention = ActiveRepositoryRoundTripVerification.ResolvedPath.IsEmpty()
|
||||
|| !ActiveRepositoryRoundTripVerification.bDidRoundTripPass;
|
||||
|
||||
const FHyperTwistTrainingCoachSessionQueueStateEntry* NextRunnableEntry =
|
||||
HyperTwistTrainingSubsystemInternal::FindNextRunnableCoachQueueEntry(ActiveCoachSessionQueueState);
|
||||
PanelState.bHasRunnableQueueEntry = NextRunnableEntry != nullptr;
|
||||
PanelState.bHasQueueWork = ActiveCoachSessionQueueState.IsStructurallyValid()
|
||||
? ActiveCoachSessionQueueState.TotalEntryCount > 0
|
||||
: ActiveCoachSessionQueueSummary.TotalEntryCount > 0;
|
||||
PanelState.bHasDeferredQueueWork = ActiveCoachSessionQueueState.IsStructurallyValid()
|
||||
? ActiveCoachSessionQueueState.DeferredEntryCount > 0
|
||||
: false;
|
||||
PanelState.TotalQueueEntryCount = ActiveCoachSessionQueueState.IsStructurallyValid()
|
||||
? ActiveCoachSessionQueueState.TotalEntryCount
|
||||
: ActiveCoachSessionQueueSummary.TotalEntryCount;
|
||||
PanelState.ReadyQueueEntryCount = ActiveCoachSessionQueueState.IsStructurallyValid()
|
||||
? ActiveCoachSessionQueueState.ReadyEntryCount
|
||||
: (ActiveCoachSessionQueueSummary.bHasImmediateQueue ? 1 : 0);
|
||||
PanelState.DeferredQueueEntryCount = ActiveCoachSessionQueueState.IsStructurallyValid()
|
||||
? ActiveCoachSessionQueueState.DeferredEntryCount
|
||||
: 0;
|
||||
PanelState.FollowUpQueueEntryCount = ActiveCoachSessionQueueState.IsStructurallyValid()
|
||||
? ActiveCoachSessionQueueState.Entries.FilterByPredicate(
|
||||
[](const FHyperTwistTrainingCoachSessionQueueStateEntry& Candidate)
|
||||
{
|
||||
return Candidate.bRequiresFollowUp;
|
||||
}
|
||||
).Num()
|
||||
: ActiveCoachSessionQueueSummary.FollowUpEntryCount;
|
||||
|
||||
const FHyperTwistTrainingDeck RecommendedDeck = BuildActiveCoachRecommendedDeck(3);
|
||||
const FHyperTwistTrainingDeck FollowUpDeck = BuildActiveCoachFollowUpDeck(3);
|
||||
PanelState.bCanStartCoachRecommendedRun = RecommendedDeck.IsStructurallyValid();
|
||||
PanelState.bCanStartCoachFollowUpRun = FollowUpDeck.IsStructurallyValid();
|
||||
PanelState.bCanStartQueuedRun = NextRunnableEntry != nullptr
|
||||
&& ((NextRunnableEntry->SourceLabel == TEXT("primary-brief") && PanelState.bCanStartCoachRecommendedRun)
|
||||
|| (NextRunnableEntry->SourceLabel == TEXT("follow-up-brief") && PanelState.bCanStartCoachFollowUpRun)
|
||||
|| HyperTwistTrainingSubsystemInternal::BuildCoachQueueDeckFromEntry(
|
||||
*NextRunnableEntry,
|
||||
ActiveRunState,
|
||||
3
|
||||
).IsStructurallyValid());
|
||||
|
||||
if (NextRunnableEntry != nullptr)
|
||||
{
|
||||
PanelState.Headline = NextRunnableEntry->Headline;
|
||||
PanelState.SecondaryLine = HyperTwistTrainingSubsystemInternal::ResolveQueueActionLabel(
|
||||
NextRunnableEntry->SourceLabel
|
||||
);
|
||||
PanelState.PrimaryActionLabel = PanelState.SecondaryLine;
|
||||
PanelState.NextQueueSourceLabel = NextRunnableEntry->SourceLabel;
|
||||
PanelState.ActiveQueueEntryId = NextRunnableEntry->EntryId;
|
||||
PanelState.FocusDeckId = NextRunnableEntry->FocusDeckId;
|
||||
PanelState.FocusMethodSegmentId = NextRunnableEntry->FocusMethodSegmentId;
|
||||
PanelState.FocusCaseCount = NextRunnableEntry->FocusCaseIds.Num();
|
||||
PanelState.SuggestedMode = NextRunnableEntry->SuggestedMode;
|
||||
PanelState.SuggestedSelectionPolicy = NextRunnableEntry->SuggestedSelectionPolicy;
|
||||
}
|
||||
else if (PanelState.bHasCoachGuidance)
|
||||
{
|
||||
PanelState.Headline = ActiveCoachBrief.Headline;
|
||||
PanelState.SecondaryLine = ActiveCoachBrief.PrimaryActionLabel;
|
||||
PanelState.PrimaryActionLabel = ActiveCoachBrief.PrimaryActionLabel;
|
||||
PanelState.FocusDeckId = ActiveCoachBrief.FocusDeckId;
|
||||
PanelState.FocusMethodSegmentId = ActiveCoachBrief.FocusMethodSegmentId;
|
||||
PanelState.FocusCaseCount = ActiveCoachBrief.FocusCaseIds.Num();
|
||||
PanelState.SuggestedMode = ActiveCoachBrief.SuggestedMode;
|
||||
PanelState.SuggestedSelectionPolicy = ActiveCoachBrief.SuggestedSelectionPolicy;
|
||||
}
|
||||
else if (PanelState.bHasFollowUpGuidance)
|
||||
{
|
||||
PanelState.Headline = ActiveCoachFollowUpBrief.Headline;
|
||||
PanelState.SecondaryLine = ActiveCoachFollowUpBrief.PrimaryActionLabel;
|
||||
PanelState.PrimaryActionLabel = ActiveCoachFollowUpBrief.PrimaryActionLabel;
|
||||
PanelState.FocusDeckId = ActiveCoachFollowUpBrief.FocusDeckId;
|
||||
PanelState.FocusMethodSegmentId = ActiveCoachFollowUpBrief.FocusMethodSegmentId;
|
||||
PanelState.FocusCaseCount = ActiveCoachFollowUpBrief.FocusCaseIds.Num();
|
||||
PanelState.SuggestedMode = ActiveCoachFollowUpBrief.SuggestedMode;
|
||||
PanelState.SuggestedSelectionPolicy = ActiveCoachFollowUpBrief.SuggestedSelectionPolicy;
|
||||
}
|
||||
else if (HasActiveRun())
|
||||
{
|
||||
PanelState.Headline = ActiveRunState.Session.SessionState == EHyperTwistTrainingSessionState::Completed
|
||||
? TEXT("Training run is complete")
|
||||
: TEXT("Training run is active");
|
||||
PanelState.SecondaryLine = ActiveRunState.ActiveDeck.Title.IsEmpty()
|
||||
? TEXT("Refresh coaching state to continue.")
|
||||
: ActiveRunState.ActiveDeck.Title;
|
||||
}
|
||||
else
|
||||
{
|
||||
PanelState.Headline = TEXT("Training state is stable");
|
||||
PanelState.SecondaryLine = TEXT("No immediate coach queue work is active.");
|
||||
}
|
||||
|
||||
PanelState.QueueStatusLine = PanelState.TotalQueueEntryCount > 0
|
||||
? FString::Printf(
|
||||
TEXT("Queue: %d total, %d ready, %d deferred"),
|
||||
PanelState.TotalQueueEntryCount,
|
||||
PanelState.ReadyQueueEntryCount,
|
||||
PanelState.DeferredQueueEntryCount
|
||||
)
|
||||
: TEXT("Queue: no queued coach work");
|
||||
PanelState.VerificationStatusLine = ActiveRepositoryRoundTripVerification.ResolvedPath.IsEmpty()
|
||||
? TEXT("Verification: not run yet")
|
||||
: (ActiveRepositoryRoundTripVerification.bDidRoundTripPass
|
||||
? TEXT("Verification: repository round-trip passed")
|
||||
: TEXT("Verification: repository round-trip needs attention"));
|
||||
return PanelState;
|
||||
}
|
||||
|
||||
FHyperTwistTrainingDeckHistorySummary UHyperTwistTrainingSubsystem::GetActiveDeckHistorySummary() const
|
||||
{
|
||||
return ActiveDeckHistorySummary;
|
||||
|
|
@ -1065,6 +1376,99 @@ FHyperTwistTrainingRunState UHyperTwistTrainingSubsystem::StartCoachFollowUpRun(
|
|||
return RunState;
|
||||
}
|
||||
|
||||
FHyperTwistTrainingRunState UHyperTwistTrainingSubsystem::StartNextQueuedCoachRun(
|
||||
const FString& SessionId,
|
||||
int32 MaxCases
|
||||
)
|
||||
{
|
||||
const FHyperTwistTrainingCoachSessionQueueStateEntry* NextRunnableEntry =
|
||||
HyperTwistTrainingSubsystemInternal::FindNextRunnableCoachQueueEntry(ActiveCoachSessionQueueState);
|
||||
if (NextRunnableEntry == nullptr)
|
||||
{
|
||||
return FHyperTwistTrainingRunState();
|
||||
}
|
||||
|
||||
if (NextRunnableEntry->SourceLabel == TEXT("primary-brief"))
|
||||
{
|
||||
return StartCoachRecommendedRun(SessionId, MaxCases);
|
||||
}
|
||||
if (NextRunnableEntry->SourceLabel == TEXT("follow-up-brief"))
|
||||
{
|
||||
return StartCoachFollowUpRun(SessionId, MaxCases);
|
||||
}
|
||||
|
||||
const FHyperTwistTrainingDeck QueueDeck = HyperTwistTrainingSubsystemInternal::BuildCoachQueueDeckFromEntry(
|
||||
*NextRunnableEntry,
|
||||
ActiveRunState,
|
||||
MaxCases
|
||||
);
|
||||
if (!QueueDeck.IsStructurallyValid())
|
||||
{
|
||||
return FHyperTwistTrainingRunState();
|
||||
}
|
||||
|
||||
const FString UserId = !ActiveCoachSessionQueueState.UserId.IsEmpty()
|
||||
? ActiveCoachSessionQueueState.UserId
|
||||
: (ActiveRunState.Session.UserId.IsEmpty() ? TEXT("local-user") : ActiveRunState.Session.UserId);
|
||||
const FHyperTwistTrainingCoachSessionQueueState QueueStateBeforeStart = ActiveCoachSessionQueueState;
|
||||
const FHyperTwistCoachBrief QueueBrief = HyperTwistTrainingSubsystemInternal::BuildCoachBriefFromQueueEntry(
|
||||
QueueStateBeforeStart,
|
||||
*NextRunnableEntry,
|
||||
QueueStateBeforeStart.ReferenceUtc
|
||||
);
|
||||
const FHyperTwistTrainingCoachActionPlan DraftActionPlan =
|
||||
UHyperTwistTrainingRepositoryLibrary::BuildCoachActionPlan(
|
||||
QueueBrief,
|
||||
FString::Printf(TEXT("coach_queue_%s_%s"), *NextRunnableEntry->EntryId, *SessionId),
|
||||
FString()
|
||||
);
|
||||
FHyperTwistTrainingRunState RunState = StartTrainingRunFromDeck(
|
||||
QueueDeck,
|
||||
UserId,
|
||||
SessionId,
|
||||
NextRunnableEntry->SuggestedMode
|
||||
);
|
||||
if (!RunState.IsStructurallyValid())
|
||||
{
|
||||
return RunState;
|
||||
}
|
||||
|
||||
if (DraftActionPlan.IsStructurallyValid())
|
||||
{
|
||||
ActiveCoachActionPlan = UHyperTwistTrainingRepositoryLibrary::ActivateCoachActionPlan(
|
||||
DraftActionPlan,
|
||||
RunState.Session.TrainingSessionId,
|
||||
RunState.Session.StartedAtUtc
|
||||
);
|
||||
TrainingRepositoryState = UHyperTwistTrainingRepositoryLibrary::UpsertCoachActionPlan(
|
||||
TrainingRepositoryState,
|
||||
ActiveCoachActionPlan
|
||||
);
|
||||
}
|
||||
|
||||
if (QueueStateBeforeStart.IsStructurallyValid())
|
||||
{
|
||||
const FHyperTwistTrainingCoachSessionQueueState UpdatedQueueState =
|
||||
UHyperTwistTrainingRepositoryLibrary::StartCoachSessionQueueEntryBySourceLabel(
|
||||
QueueStateBeforeStart,
|
||||
NextRunnableEntry->SourceLabel,
|
||||
RunState.Session.TrainingSessionId,
|
||||
RunState.Session.StartedAtUtc
|
||||
);
|
||||
if (UpdatedQueueState.IsStructurallyValid())
|
||||
{
|
||||
TrainingRepositoryState = UHyperTwistTrainingRepositoryLibrary::UpsertCoachSessionQueueState(
|
||||
TrainingRepositoryState,
|
||||
UpdatedQueueState
|
||||
);
|
||||
ActiveCoachSessionQueueState = UpdatedQueueState;
|
||||
}
|
||||
}
|
||||
|
||||
RefreshRepositoryViews();
|
||||
return RunState;
|
||||
}
|
||||
|
||||
FHyperTwistTrainingRunState UHyperTwistTrainingSubsystem::StartTrainingRunFromDeck(
|
||||
const FHyperTwistTrainingDeck& Deck,
|
||||
const FString& UserId,
|
||||
|
|
|
|||
|
|
@ -53,6 +53,9 @@ public:
|
|||
UPROPERTY(VisibleAnywhere, BlueprintReadOnly, Category = "HyperTwist|Training|Coach")
|
||||
FHyperTwistTrainingCoachSessionQueueExecutionSummary CachedCoachQueueExecutionSummary;
|
||||
|
||||
UPROPERTY(VisibleAnywhere, BlueprintReadOnly, Category = "HyperTwist|Training|Coach")
|
||||
FHyperTwistTrainingCoachPanelState CachedCoachPanelState;
|
||||
|
||||
UPROPERTY(VisibleAnywhere, BlueprintReadOnly, Category = "HyperTwist|Training|Coach")
|
||||
FHyperTwistTrainingDeck CachedCoachRecommendedDeck;
|
||||
|
||||
|
|
@ -103,6 +106,9 @@ public:
|
|||
UFUNCTION(BlueprintCallable, Category = "HyperTwist|Training|Coach")
|
||||
FHyperTwistTrainingRunState StartCoachFollowUpRun(int32 MaxCases);
|
||||
|
||||
UFUNCTION(BlueprintCallable, Category = "HyperTwist|Training|Coach")
|
||||
FHyperTwistTrainingRunState StartNextQueuedCoachRun(int32 MaxCases);
|
||||
|
||||
UFUNCTION(BlueprintCallable, Category = "HyperTwist|Training")
|
||||
FHyperTwistTrainingRepositoryRoundTripVerification VerifyTrainingRepositoryRoundTrip();
|
||||
};
|
||||
|
|
|
|||
|
|
@ -50,6 +50,9 @@ public:
|
|||
UObject* WorldContextObject
|
||||
);
|
||||
|
||||
UFUNCTION(BlueprintPure, Category = "HyperTwist|Training|Coach", meta = (WorldContext = "WorldContextObject"))
|
||||
static FHyperTwistTrainingCoachPanelState GetActiveTrainingCoachPanelState(UObject* WorldContextObject);
|
||||
|
||||
UFUNCTION(BlueprintPure, Category = "HyperTwist|Training", meta = (WorldContext = "WorldContextObject"))
|
||||
static FHyperTwistTrainingDeckHistorySummary GetActiveTrainingDeckHistorySummary(UObject* WorldContextObject);
|
||||
|
||||
|
|
@ -414,6 +417,13 @@ public:
|
|||
int32 MaxCases
|
||||
);
|
||||
|
||||
UFUNCTION(BlueprintCallable, Category = "HyperTwist|Training|Coach", meta = (WorldContext = "WorldContextObject"))
|
||||
static FHyperTwistTrainingRunState StartNextQueuedCoachRun(
|
||||
UObject* WorldContextObject,
|
||||
const FString& SessionId,
|
||||
int32 MaxCases
|
||||
);
|
||||
|
||||
UFUNCTION(BlueprintCallable, Category = "HyperTwist|Training", meta = (WorldContext = "WorldContextObject"))
|
||||
static FHyperTwistTrainingRunState StartSampleClassicTrainingRun(
|
||||
UObject* WorldContextObject,
|
||||
|
|
|
|||
|
|
@ -55,6 +55,9 @@ public:
|
|||
UPROPERTY(VisibleAnywhere, BlueprintReadOnly, Category = "HyperTwist|Training|Coach")
|
||||
FHyperTwistTrainingCoachSessionQueueExecutionSummary CachedCoachQueueExecutionSummary;
|
||||
|
||||
UPROPERTY(VisibleAnywhere, BlueprintReadOnly, Category = "HyperTwist|Training|Coach")
|
||||
FHyperTwistTrainingCoachPanelState CachedCoachPanelState;
|
||||
|
||||
UPROPERTY(VisibleAnywhere, BlueprintReadOnly, Category = "HyperTwist|Training|Coach")
|
||||
FHyperTwistTrainingDeck CachedCoachRecommendedDeck;
|
||||
|
||||
|
|
@ -105,6 +108,9 @@ public:
|
|||
UFUNCTION(BlueprintCallable, Category = "HyperTwist|Training|Coach")
|
||||
FHyperTwistTrainingRunState StartCoachFollowUpTrainingRun(int32 MaxCases);
|
||||
|
||||
UFUNCTION(BlueprintCallable, Category = "HyperTwist|Training|Coach")
|
||||
FHyperTwistTrainingRunState StartNextQueuedCoachTrainingRun(int32 MaxCases);
|
||||
|
||||
UFUNCTION(BlueprintCallable, Category = "HyperTwist|Training")
|
||||
FHyperTwistTrainingRepositoryRoundTripVerification VerifyRepositoryRoundTrip();
|
||||
};
|
||||
|
|
|
|||
|
|
@ -41,6 +41,9 @@ public:
|
|||
UFUNCTION(BlueprintPure, Category = "HyperTwist|Training")
|
||||
FHyperTwistTrainingRepositoryRoundTripVerification GetActiveRepositoryRoundTripVerification() const;
|
||||
|
||||
UFUNCTION(BlueprintPure, Category = "HyperTwist|Training|Coach")
|
||||
FHyperTwistTrainingCoachPanelState GetActiveCoachPanelState() const;
|
||||
|
||||
UFUNCTION(BlueprintPure, Category = "HyperTwist|Training")
|
||||
FHyperTwistTrainingDeckHistorySummary GetActiveDeckHistorySummary() const;
|
||||
|
||||
|
|
@ -333,6 +336,12 @@ public:
|
|||
int32 MaxCases
|
||||
);
|
||||
|
||||
UFUNCTION(BlueprintCallable, Category = "HyperTwist|Training|Coach")
|
||||
FHyperTwistTrainingRunState StartNextQueuedCoachRun(
|
||||
const FString& SessionId,
|
||||
int32 MaxCases
|
||||
);
|
||||
|
||||
UFUNCTION(BlueprintCallable, Category = "HyperTwist|Training")
|
||||
FHyperTwistTrainingRunState StartTrainingRunFromDeck(
|
||||
const FHyperTwistTrainingDeck& Deck,
|
||||
|
|
|
|||
|
|
@ -4182,3 +4182,106 @@ struct FHyperTwistTrainingRepositoryRoundTripVerification
|
|||
return !ResolvedPath.IsEmpty() && bSaveSucceeded && bLoadSucceeded;
|
||||
}
|
||||
};
|
||||
|
||||
USTRUCT(BlueprintType)
|
||||
struct FHyperTwistTrainingCoachPanelState
|
||||
{
|
||||
GENERATED_BODY()
|
||||
|
||||
UPROPERTY(EditAnywhere, BlueprintReadWrite, Category = "HyperTwist")
|
||||
FString UserId;
|
||||
|
||||
UPROPERTY(EditAnywhere, BlueprintReadWrite, Category = "HyperTwist")
|
||||
FString Headline;
|
||||
|
||||
UPROPERTY(EditAnywhere, BlueprintReadWrite, Category = "HyperTwist")
|
||||
FString SecondaryLine;
|
||||
|
||||
UPROPERTY(EditAnywhere, BlueprintReadWrite, Category = "HyperTwist")
|
||||
FString QueueStatusLine;
|
||||
|
||||
UPROPERTY(EditAnywhere, BlueprintReadWrite, Category = "HyperTwist")
|
||||
FString VerificationStatusLine;
|
||||
|
||||
UPROPERTY(EditAnywhere, BlueprintReadWrite, Category = "HyperTwist")
|
||||
FString PrimaryActionLabel;
|
||||
|
||||
UPROPERTY(EditAnywhere, BlueprintReadWrite, Category = "HyperTwist")
|
||||
FString NextQueueSourceLabel;
|
||||
|
||||
UPROPERTY(EditAnywhere, BlueprintReadWrite, Category = "HyperTwist")
|
||||
FString ActiveQueueEntryId;
|
||||
|
||||
UPROPERTY(EditAnywhere, BlueprintReadWrite, Category = "HyperTwist")
|
||||
FString FocusDeckId;
|
||||
|
||||
UPROPERTY(EditAnywhere, BlueprintReadWrite, Category = "HyperTwist")
|
||||
FString FocusMethodSegmentId;
|
||||
|
||||
UPROPERTY(EditAnywhere, BlueprintReadWrite, Category = "HyperTwist")
|
||||
EHyperTwistTrainingDeliveryMode SuggestedMode = EHyperTwistTrainingDeliveryMode::Timer;
|
||||
|
||||
UPROPERTY(EditAnywhere, BlueprintReadWrite, Category = "HyperTwist")
|
||||
EHyperTwistTrainingSelectionPolicy SuggestedSelectionPolicy = EHyperTwistTrainingSelectionPolicy::Weighted;
|
||||
|
||||
UPROPERTY(EditAnywhere, BlueprintReadWrite, Category = "HyperTwist")
|
||||
int32 SignalCount = 0;
|
||||
|
||||
UPROPERTY(EditAnywhere, BlueprintReadWrite, Category = "HyperTwist")
|
||||
int32 FocusCaseCount = 0;
|
||||
|
||||
UPROPERTY(EditAnywhere, BlueprintReadWrite, Category = "HyperTwist")
|
||||
int32 TotalQueueEntryCount = 0;
|
||||
|
||||
UPROPERTY(EditAnywhere, BlueprintReadWrite, Category = "HyperTwist")
|
||||
int32 ReadyQueueEntryCount = 0;
|
||||
|
||||
UPROPERTY(EditAnywhere, BlueprintReadWrite, Category = "HyperTwist")
|
||||
int32 DeferredQueueEntryCount = 0;
|
||||
|
||||
UPROPERTY(EditAnywhere, BlueprintReadWrite, Category = "HyperTwist")
|
||||
int32 FollowUpQueueEntryCount = 0;
|
||||
|
||||
UPROPERTY(EditAnywhere, BlueprintReadWrite, Category = "HyperTwist")
|
||||
bool bHasActiveRun = false;
|
||||
|
||||
UPROPERTY(EditAnywhere, BlueprintReadWrite, Category = "HyperTwist")
|
||||
bool bHasCoachGuidance = false;
|
||||
|
||||
UPROPERTY(EditAnywhere, BlueprintReadWrite, Category = "HyperTwist")
|
||||
bool bHasFollowUpGuidance = false;
|
||||
|
||||
UPROPERTY(EditAnywhere, BlueprintReadWrite, Category = "HyperTwist")
|
||||
bool bHasQueueWork = false;
|
||||
|
||||
UPROPERTY(EditAnywhere, BlueprintReadWrite, Category = "HyperTwist")
|
||||
bool bHasDeferredQueueWork = false;
|
||||
|
||||
UPROPERTY(EditAnywhere, BlueprintReadWrite, Category = "HyperTwist")
|
||||
bool bHasRunnableQueueEntry = false;
|
||||
|
||||
UPROPERTY(EditAnywhere, BlueprintReadWrite, Category = "HyperTwist")
|
||||
bool bCanStartCoachRecommendedRun = false;
|
||||
|
||||
UPROPERTY(EditAnywhere, BlueprintReadWrite, Category = "HyperTwist")
|
||||
bool bCanStartCoachFollowUpRun = false;
|
||||
|
||||
UPROPERTY(EditAnywhere, BlueprintReadWrite, Category = "HyperTwist")
|
||||
bool bCanStartQueuedRun = false;
|
||||
|
||||
UPROPERTY(EditAnywhere, BlueprintReadWrite, Category = "HyperTwist")
|
||||
bool bRepositoryVerificationPassed = false;
|
||||
|
||||
UPROPERTY(EditAnywhere, BlueprintReadWrite, Category = "HyperTwist")
|
||||
bool bRepositoryVerificationNeedsAttention = true;
|
||||
|
||||
bool IsStructurallyValid() const
|
||||
{
|
||||
return !Headline.IsEmpty()
|
||||
|| bHasCoachGuidance
|
||||
|| bHasFollowUpGuidance
|
||||
|| bHasQueueWork
|
||||
|| bHasRunnableQueueEntry
|
||||
|| bHasActiveRun;
|
||||
}
|
||||
};
|
||||
|
|
|
|||
Loading…
Add table
Reference in a new issue