Add coach schedule horizon summaries

This commit is contained in:
axiomlogicnexus 2026-04-26 04:09:24 +02:00
parent 8df9776fcf
commit 5ec0257f1f
8 changed files with 222 additions and 0 deletions

View file

@ -1330,6 +1330,15 @@ FHyperTwistTrainingCoachSessionQueueState UHyperTwistContractLibrary::MakeSample
);
}
FHyperTwistCoachScheduleHorizonSummary UHyperTwistContractLibrary::MakeSampleTrainingCoachScheduleHorizonSummary()
{
return UHyperTwistTrainingCoachLibrary::DeriveCoachScheduleHorizonSummary(
MakeSampleTrainingCoachSessionQueueState(),
MakeSampleTrainingCoachArchivePolicySummary(),
TEXT("2026-04-28T12:12:00Z")
);
}
bool UHyperTwistContractLibrary::IsPuzzleStateStructurallyValid(const FHyperTwistPuzzleState& State)
{
return State.IsStructurallyValid();

View file

@ -714,3 +714,97 @@ FHyperTwistCoachSessionQueueSummary UHyperTwistTrainingCoachLibrary::DeriveCoach
return Summary;
}
FHyperTwistCoachScheduleHorizonSummary UHyperTwistTrainingCoachLibrary::DeriveCoachScheduleHorizonSummary(
const FHyperTwistTrainingCoachSessionQueueState& QueueState,
const FHyperTwistTrainingCoachArchivePolicySummary& ArchivePolicySummary,
const FString& ReferenceUtc
)
{
FHyperTwistCoachScheduleHorizonSummary Summary;
if (!QueueState.IsStructurallyValid())
{
return Summary;
}
Summary.UserId = QueueState.UserId;
Summary.ReferenceUtc = !ReferenceUtc.IsEmpty()
? ReferenceUtc
: (!QueueState.LastUpdatedAtUtc.IsEmpty() ? QueueState.LastUpdatedAtUtc : QueueState.ReferenceUtc);
Summary.QueueId = QueueState.QueueId;
Summary.FocusDeckId = QueueState.FocusDeckId;
Summary.FocusMethodSegmentId = QueueState.FocusMethodSegmentId;
Summary.ActiveEntryId = QueueState.ActiveEntryId;
for (const FHyperTwistTrainingCoachSessionQueueStateEntry& Entry : QueueState.Entries)
{
if (!Entry.IsStructurallyValid())
{
continue;
}
const bool bIsImmediateEntry =
Entry.EntryState == EHyperTwistTrainingCoachSessionQueueEntryState::Ready
|| Entry.EntryState == EHyperTwistTrainingCoachSessionQueueEntryState::InProgress;
const bool bIsDeferredEntry = Entry.EntryState == EHyperTwistTrainingCoachSessionQueueEntryState::Pending;
Summary.ImmediateEntryCount += bIsImmediateEntry ? 1 : 0;
Summary.DeferredEntryCount += bIsDeferredEntry ? 1 : 0;
Summary.ArchiveAwareEntryCount += Entry.bRequiresArchiveRetentionTuning ? 1 : 0;
Summary.FollowUpEntryCount += Entry.bRequiresFollowUp ? 1 : 0;
Summary.PinnedEntryCount += Entry.bPinned ? 1 : 0;
if (Entry.EntryState == EHyperTwistTrainingCoachSessionQueueEntryState::InProgress)
{
Summary.bHasInProgressEntry = true;
}
if (bIsImmediateEntry && Summary.NextReadyEntryId.IsEmpty())
{
Summary.NextReadyEntryId = Entry.EntryId;
}
if (bIsDeferredEntry && Summary.NextDeferredEntryId.IsEmpty())
{
Summary.NextDeferredEntryId = Entry.EntryId;
}
if (Entry.bRequiresArchiveRetentionTuning && Summary.NextArchiveEntryId.IsEmpty())
{
Summary.NextArchiveEntryId = Entry.EntryId;
}
if (Entry.bRequiresFollowUp && Summary.NextFollowUpEntryId.IsEmpty())
{
Summary.NextFollowUpEntryId = Entry.EntryId;
}
}
Summary.bHasImmediateWindow = Summary.ImmediateEntryCount > 0;
Summary.bHasDeferredWindow = Summary.DeferredEntryCount > 0;
Summary.bNeedsArchiveWindow = QueueState.bNeedsArchiveAwareSequencing
&& (Summary.ArchiveAwareEntryCount > 0 || ArchivePolicySummary.bNeedsArchiveRetentionTuning);
Summary.bHasBlockedFollowUp = Summary.FollowUpEntryCount > 0
&& Summary.NextFollowUpEntryId != Summary.NextReadyEntryId
&& (Summary.bHasImmediateWindow || Summary.bNeedsArchiveWindow);
if (Summary.bHasInProgressEntry || Summary.bHasImmediateWindow)
{
Summary.CurrentHorizonLabel = TEXT("immediate");
}
else if (Summary.bNeedsArchiveWindow)
{
Summary.CurrentHorizonLabel = TEXT("archive-aware");
}
else if (!Summary.NextFollowUpEntryId.IsEmpty())
{
Summary.CurrentHorizonLabel = TEXT("follow-up");
}
else if (!Summary.NextDeferredEntryId.IsEmpty())
{
Summary.CurrentHorizonLabel = TEXT("deferred");
}
else
{
Summary.CurrentHorizonLabel = TEXT("idle");
}
return Summary;
}

View file

@ -381,6 +381,18 @@ FHyperTwistTrainingCoachSessionQueueState UHyperTwistTrainingRuntimeLibrary::Get
return FHyperTwistTrainingCoachSessionQueueState();
}
FHyperTwistCoachScheduleHorizonSummary UHyperTwistTrainingRuntimeLibrary::GetActiveTrainingCoachScheduleHorizonSummary(
UObject* WorldContextObject
)
{
if (UHyperTwistTrainingSubsystem* TrainingSubsystem = GetTrainingSubsystem(WorldContextObject))
{
return TrainingSubsystem->GetActiveCoachScheduleHorizonSummary();
}
return FHyperTwistCoachScheduleHorizonSummary();
}
TArray<FHyperTwistTrainingMethodSegment> UHyperTwistTrainingRuntimeLibrary::GetActiveTrainingLearnerMethodSegments(UObject* WorldContextObject)
{
if (UHyperTwistTrainingSubsystem* TrainingSubsystem = GetTrainingSubsystem(WorldContextObject))

View file

@ -173,6 +173,11 @@ FHyperTwistTrainingCoachSessionQueueState UHyperTwistTrainingSubsystem::GetActiv
return ActiveCoachSessionQueueState;
}
FHyperTwistCoachScheduleHorizonSummary UHyperTwistTrainingSubsystem::GetActiveCoachScheduleHorizonSummary() const
{
return ActiveCoachScheduleHorizonSummary;
}
TArray<FHyperTwistTrainingMethodSegment> UHyperTwistTrainingSubsystem::GetActiveLearnerMethodSegments() const
{
return ActiveLearnerMethodSegments;
@ -1315,6 +1320,7 @@ void UHyperTwistTrainingSubsystem::RefreshRepositoryViews()
ActiveCoachFollowUpBrief = FHyperTwistCoachBrief();
ActiveCoachSessionQueueSummary = FHyperTwistCoachSessionQueueSummary();
ActiveCoachSessionQueueState = FHyperTwistTrainingCoachSessionQueueState();
ActiveCoachScheduleHorizonSummary = FHyperTwistCoachScheduleHorizonSummary();
ActiveLearnerMethodSegments.Reset();
ActiveLearnerPresets.Reset();
ActiveTrainingSessionTemplates.Reset();
@ -1517,10 +1523,16 @@ void UHyperTwistTrainingSubsystem::RefreshRepositoryViews()
ActiveCoachSessionQueueState
);
}
ActiveCoachScheduleHorizonSummary = UHyperTwistTrainingCoachLibrary::DeriveCoachScheduleHorizonSummary(
ActiveCoachSessionQueueState,
ActiveCoachArchivePolicySummary,
ActiveLearnerDeckStateSummary.ReferenceUtc
);
}
else
{
ActiveCoachSessionQueueState = FHyperTwistTrainingCoachSessionQueueState();
ActiveCoachScheduleHorizonSummary = FHyperTwistCoachScheduleHorizonSummary();
}
}

View file

@ -212,6 +212,9 @@ public:
UFUNCTION(BlueprintPure, Category = "HyperTwist|Training|Coach")
static FHyperTwistTrainingCoachSessionQueueState MakeSampleTrainingCoachSessionQueueState();
UFUNCTION(BlueprintPure, Category = "HyperTwist|Training|Coach")
static FHyperTwistCoachScheduleHorizonSummary MakeSampleTrainingCoachScheduleHorizonSummary();
UFUNCTION(BlueprintPure, Category = "HyperTwist|Validation")
static bool IsPuzzleStateStructurallyValid(const FHyperTwistPuzzleState& State);

View file

@ -237,6 +237,80 @@ struct FHyperTwistCoachSessionQueueSummary
}
};
USTRUCT(BlueprintType)
struct FHyperTwistCoachScheduleHorizonSummary
{
GENERATED_BODY()
UPROPERTY(EditAnywhere, BlueprintReadWrite, Category = "HyperTwist")
FString UserId;
UPROPERTY(EditAnywhere, BlueprintReadWrite, Category = "HyperTwist")
FString ReferenceUtc;
UPROPERTY(EditAnywhere, BlueprintReadWrite, Category = "HyperTwist")
FString QueueId;
UPROPERTY(EditAnywhere, BlueprintReadWrite, Category = "HyperTwist")
FString FocusDeckId;
UPROPERTY(EditAnywhere, BlueprintReadWrite, Category = "HyperTwist")
FString FocusMethodSegmentId;
UPROPERTY(EditAnywhere, BlueprintReadWrite, Category = "HyperTwist")
FString ActiveEntryId;
UPROPERTY(EditAnywhere, BlueprintReadWrite, Category = "HyperTwist")
FString NextReadyEntryId;
UPROPERTY(EditAnywhere, BlueprintReadWrite, Category = "HyperTwist")
FString NextDeferredEntryId;
UPROPERTY(EditAnywhere, BlueprintReadWrite, Category = "HyperTwist")
FString NextArchiveEntryId;
UPROPERTY(EditAnywhere, BlueprintReadWrite, Category = "HyperTwist")
FString NextFollowUpEntryId;
UPROPERTY(EditAnywhere, BlueprintReadWrite, Category = "HyperTwist")
FString CurrentHorizonLabel;
UPROPERTY(EditAnywhere, BlueprintReadWrite, Category = "HyperTwist")
int32 ImmediateEntryCount = 0;
UPROPERTY(EditAnywhere, BlueprintReadWrite, Category = "HyperTwist")
int32 DeferredEntryCount = 0;
UPROPERTY(EditAnywhere, BlueprintReadWrite, Category = "HyperTwist")
int32 ArchiveAwareEntryCount = 0;
UPROPERTY(EditAnywhere, BlueprintReadWrite, Category = "HyperTwist")
int32 FollowUpEntryCount = 0;
UPROPERTY(EditAnywhere, BlueprintReadWrite, Category = "HyperTwist")
int32 PinnedEntryCount = 0;
UPROPERTY(EditAnywhere, BlueprintReadWrite, Category = "HyperTwist")
bool bHasImmediateWindow = false;
UPROPERTY(EditAnywhere, BlueprintReadWrite, Category = "HyperTwist")
bool bHasDeferredWindow = false;
UPROPERTY(EditAnywhere, BlueprintReadWrite, Category = "HyperTwist")
bool bNeedsArchiveWindow = false;
UPROPERTY(EditAnywhere, BlueprintReadWrite, Category = "HyperTwist")
bool bHasBlockedFollowUp = false;
UPROPERTY(EditAnywhere, BlueprintReadWrite, Category = "HyperTwist")
bool bHasInProgressEntry = false;
bool IsStructurallyValid() const
{
return !UserId.IsEmpty() && !QueueId.IsEmpty();
}
};
UCLASS()
class UNREALHYPERTWIST_API UHyperTwistTrainingCoachLibrary : public UBlueprintFunctionLibrary
{
@ -281,4 +355,11 @@ public:
const FHyperTwistTrainingCoachArchivePolicySummary& ArchivePolicySummary,
const FHyperTwistTrainingReviewProgramSummary& ReviewProgramSummary
);
UFUNCTION(BlueprintPure, Category = "HyperTwist|Training|Coach")
static FHyperTwistCoachScheduleHorizonSummary DeriveCoachScheduleHorizonSummary(
const FHyperTwistTrainingCoachSessionQueueState& QueueState,
const FHyperTwistTrainingCoachArchivePolicySummary& ArchivePolicySummary,
const FString& ReferenceUtc
);
};

View file

@ -125,6 +125,11 @@ public:
UObject* WorldContextObject
);
UFUNCTION(BlueprintPure, Category = "HyperTwist|Training|Coach", meta = (WorldContext = "WorldContextObject"))
static FHyperTwistCoachScheduleHorizonSummary GetActiveTrainingCoachScheduleHorizonSummary(
UObject* WorldContextObject
);
UFUNCTION(BlueprintPure, Category = "HyperTwist|Training", meta = (WorldContext = "WorldContextObject"))
static TArray<FHyperTwistTrainingMethodSegment> GetActiveTrainingLearnerMethodSegments(UObject* WorldContextObject);

View file

@ -110,6 +110,9 @@ public:
UFUNCTION(BlueprintPure, Category = "HyperTwist|Training|Coach")
FHyperTwistTrainingCoachSessionQueueState GetActiveCoachSessionQueueState() const;
UFUNCTION(BlueprintPure, Category = "HyperTwist|Training|Coach")
FHyperTwistCoachScheduleHorizonSummary GetActiveCoachScheduleHorizonSummary() const;
UFUNCTION(BlueprintPure, Category = "HyperTwist|Training")
TArray<FHyperTwistTrainingMethodSegment> GetActiveLearnerMethodSegments() const;
@ -475,6 +478,9 @@ private:
UPROPERTY()
FHyperTwistTrainingCoachSessionQueueState ActiveCoachSessionQueueState;
UPROPERTY()
FHyperTwistCoachScheduleHorizonSummary ActiveCoachScheduleHorizonSummary;
UPROPERTY()
TArray<FHyperTwistTrainingMethodSegment> ActiveLearnerMethodSegments;