Add coach queue execution analytics
This commit is contained in:
parent
1a309c862c
commit
f311b65f84
9 changed files with 845 additions and 4 deletions
|
|
@ -1367,6 +1367,72 @@ FHyperTwistCoachSchedulePolicySummary UHyperTwistContractLibrary::MakeSampleTrai
|
|||
);
|
||||
}
|
||||
|
||||
FHyperTwistTrainingCoachSessionQueueExecutionSummary UHyperTwistContractLibrary::MakeSampleTrainingCoachSessionQueueExecutionSummary()
|
||||
{
|
||||
FHyperTwistTrainingRepositoryState RepositoryState = MakeSampleTrainingRepositoryState();
|
||||
FHyperTwistTrainingCoachSessionQueueState QueueState =
|
||||
UHyperTwistTrainingRepositoryLibrary::BuildCoachSessionQueueState(
|
||||
MakeSampleTrainingCoachSessionQueueSummary(),
|
||||
TEXT("coach_queue_active"),
|
||||
TEXT("2026-04-28T12:12:00Z")
|
||||
);
|
||||
RepositoryState = UHyperTwistTrainingRepositoryLibrary::UpsertCoachSessionQueueState(
|
||||
RepositoryState,
|
||||
QueueState
|
||||
);
|
||||
QueueState = UHyperTwistTrainingRepositoryLibrary::DeferCoachSessionQueueEntry(
|
||||
QueueState,
|
||||
TEXT("coach_queue_follow_up"),
|
||||
TEXT("2026-04-28T18:00:00Z"),
|
||||
TEXT("follow-up-held-behind-primary"),
|
||||
false,
|
||||
TEXT("2026-04-28T12:13:00Z")
|
||||
);
|
||||
RepositoryState = UHyperTwistTrainingRepositoryLibrary::UpsertCoachSessionQueueState(
|
||||
RepositoryState,
|
||||
QueueState
|
||||
);
|
||||
QueueState = UHyperTwistTrainingRepositoryLibrary::DeferCoachSessionQueueEntry(
|
||||
QueueState,
|
||||
TEXT("coach_queue_archive"),
|
||||
TEXT("2026-04-29T09:00:00Z"),
|
||||
TEXT("archive-retention-window"),
|
||||
true,
|
||||
TEXT("2026-04-28T12:14:00Z")
|
||||
);
|
||||
RepositoryState = UHyperTwistTrainingRepositoryLibrary::UpsertCoachSessionQueueState(
|
||||
RepositoryState,
|
||||
QueueState
|
||||
);
|
||||
QueueState = UHyperTwistTrainingRepositoryLibrary::StartCoachSessionQueueEntryBySourceLabel(
|
||||
QueueState,
|
||||
TEXT("primary-brief"),
|
||||
TEXT("coach_training_session_queued_01"),
|
||||
TEXT("2026-04-28T12:20:00Z")
|
||||
);
|
||||
RepositoryState = UHyperTwistTrainingRepositoryLibrary::UpsertCoachSessionQueueState(
|
||||
RepositoryState,
|
||||
QueueState
|
||||
);
|
||||
QueueState = UHyperTwistTrainingRepositoryLibrary::FinalizeCoachSessionQueueEntry(
|
||||
QueueState,
|
||||
TEXT("coach_training_session_queued_01"),
|
||||
TEXT("2026-04-28T12:34:00Z"),
|
||||
false
|
||||
);
|
||||
RepositoryState = UHyperTwistTrainingRepositoryLibrary::UpsertCoachSessionQueueState(
|
||||
RepositoryState,
|
||||
QueueState
|
||||
);
|
||||
|
||||
return UHyperTwistTrainingRepositoryLibrary::DeriveCoachSessionQueueExecutionSummary(
|
||||
RepositoryState,
|
||||
TEXT("local-user"),
|
||||
TEXT("coach_queue_active"),
|
||||
TEXT("2026-04-28T12:40:00Z")
|
||||
);
|
||||
}
|
||||
|
||||
bool UHyperTwistContractLibrary::IsPuzzleStateStructurallyValid(const FHyperTwistPuzzleState& State)
|
||||
{
|
||||
return State.IsStructurallyValid();
|
||||
|
|
|
|||
|
|
@ -704,6 +704,147 @@ namespace HyperTwistTrainingRepositoryLibraryInternal
|
|||
return NormalizedState;
|
||||
}
|
||||
|
||||
bool DidCoachSessionQueueEntryChange(
|
||||
const FHyperTwistTrainingCoachSessionQueueStateEntry& PreviousEntry,
|
||||
const FHyperTwistTrainingCoachSessionQueueStateEntry& CurrentEntry
|
||||
)
|
||||
{
|
||||
return PreviousEntry.WorkFingerprint != CurrentEntry.WorkFingerprint
|
||||
|| PreviousEntry.EntryState != CurrentEntry.EntryState
|
||||
|| PreviousEntry.ScheduledAtUtc != CurrentEntry.ScheduledAtUtc
|
||||
|| PreviousEntry.DeferredUntilUtc != CurrentEntry.DeferredUntilUtc
|
||||
|| PreviousEntry.DeferredAtUtc != CurrentEntry.DeferredAtUtc
|
||||
|| PreviousEntry.DeferralReasonLabel != CurrentEntry.DeferralReasonLabel
|
||||
|| PreviousEntry.StartedAtUtc != CurrentEntry.StartedAtUtc
|
||||
|| PreviousEntry.CompletedAtUtc != CurrentEntry.CompletedAtUtc
|
||||
|| PreviousEntry.RescheduleCount != CurrentEntry.RescheduleCount
|
||||
|| PreviousEntry.SourceTrainingSessionId != CurrentEntry.SourceTrainingSessionId
|
||||
|| PreviousEntry.bWasManuallyRescheduled != CurrentEntry.bWasManuallyRescheduled;
|
||||
}
|
||||
|
||||
EHyperTwistTrainingCoachSessionQueueEventKind DeriveCoachSessionQueueEventKind(
|
||||
const FHyperTwistTrainingCoachSessionQueueStateEntry* PreviousEntry,
|
||||
const FHyperTwistTrainingCoachSessionQueueStateEntry& CurrentEntry
|
||||
)
|
||||
{
|
||||
if (CurrentEntry.EntryState == EHyperTwistTrainingCoachSessionQueueEntryState::InProgress)
|
||||
{
|
||||
return EHyperTwistTrainingCoachSessionQueueEventKind::Started;
|
||||
}
|
||||
if (CurrentEntry.EntryState == EHyperTwistTrainingCoachSessionQueueEntryState::Deferred)
|
||||
{
|
||||
return EHyperTwistTrainingCoachSessionQueueEventKind::Deferred;
|
||||
}
|
||||
if (CurrentEntry.EntryState == EHyperTwistTrainingCoachSessionQueueEntryState::Completed)
|
||||
{
|
||||
return EHyperTwistTrainingCoachSessionQueueEventKind::Completed;
|
||||
}
|
||||
if (CurrentEntry.EntryState == EHyperTwistTrainingCoachSessionQueueEntryState::Skipped)
|
||||
{
|
||||
return EHyperTwistTrainingCoachSessionQueueEventKind::Skipped;
|
||||
}
|
||||
if (CurrentEntry.EntryState == EHyperTwistTrainingCoachSessionQueueEntryState::Expired)
|
||||
{
|
||||
return EHyperTwistTrainingCoachSessionQueueEventKind::Expired;
|
||||
}
|
||||
if (CurrentEntry.EntryState == EHyperTwistTrainingCoachSessionQueueEntryState::Ready)
|
||||
{
|
||||
return PreviousEntry != nullptr
|
||||
&& PreviousEntry->EntryState == EHyperTwistTrainingCoachSessionQueueEntryState::Deferred
|
||||
? EHyperTwistTrainingCoachSessionQueueEventKind::Promoted
|
||||
: EHyperTwistTrainingCoachSessionQueueEventKind::Ready;
|
||||
}
|
||||
|
||||
return EHyperTwistTrainingCoachSessionQueueEventKind::Queued;
|
||||
}
|
||||
|
||||
FString ResolveCoachSessionQueueHistoryEventAtUtc(
|
||||
const FHyperTwistTrainingCoachSessionQueueState& QueueState,
|
||||
const FHyperTwistTrainingCoachSessionQueueStateEntry& Entry,
|
||||
const EHyperTwistTrainingCoachSessionQueueEventKind EventKind
|
||||
)
|
||||
{
|
||||
switch (EventKind)
|
||||
{
|
||||
case EHyperTwistTrainingCoachSessionQueueEventKind::Started:
|
||||
return ResolveReferenceUtc(!Entry.StartedAtUtc.IsEmpty() ? Entry.StartedAtUtc : QueueState.LastUpdatedAtUtc);
|
||||
case EHyperTwistTrainingCoachSessionQueueEventKind::Deferred:
|
||||
return ResolveReferenceUtc(!Entry.DeferredAtUtc.IsEmpty() ? Entry.DeferredAtUtc : QueueState.LastUpdatedAtUtc);
|
||||
case EHyperTwistTrainingCoachSessionQueueEventKind::Completed:
|
||||
case EHyperTwistTrainingCoachSessionQueueEventKind::Skipped:
|
||||
case EHyperTwistTrainingCoachSessionQueueEventKind::Expired:
|
||||
return ResolveReferenceUtc(!Entry.CompletedAtUtc.IsEmpty() ? Entry.CompletedAtUtc : QueueState.LastUpdatedAtUtc);
|
||||
case EHyperTwistTrainingCoachSessionQueueEventKind::Promoted:
|
||||
case EHyperTwistTrainingCoachSessionQueueEventKind::Ready:
|
||||
return ResolveReferenceUtc(!Entry.ScheduledAtUtc.IsEmpty() ? Entry.ScheduledAtUtc : QueueState.LastUpdatedAtUtc);
|
||||
default:
|
||||
return ResolveReferenceUtc(QueueState.LastUpdatedAtUtc);
|
||||
}
|
||||
}
|
||||
|
||||
FString MakeCoachSessionQueueHistoryEntryId(
|
||||
const FHyperTwistTrainingCoachSessionQueueHistoryEntry& Entry
|
||||
)
|
||||
{
|
||||
return FString::Printf(
|
||||
TEXT("%s|%s|%d|%s|%s|%s"),
|
||||
*Entry.QueueId,
|
||||
*Entry.EntryId,
|
||||
static_cast<int32>(Entry.EventKind),
|
||||
*Entry.EventAtUtc,
|
||||
*Entry.WorkFingerprint,
|
||||
*Entry.SourceTrainingSessionId
|
||||
);
|
||||
}
|
||||
|
||||
FHyperTwistTrainingCoachSessionQueueHistoryEntry NormalizeCoachSessionQueueHistoryEntry(
|
||||
const FHyperTwistTrainingCoachSessionQueueHistoryEntry& HistoryEntry
|
||||
)
|
||||
{
|
||||
FHyperTwistTrainingCoachSessionQueueHistoryEntry NormalizedEntry = HistoryEntry;
|
||||
NormalizedEntry.EventAtUtc = ResolveReferenceUtc(NormalizedEntry.EventAtUtc);
|
||||
NormalizedEntry.FocusCaseIds = DeduplicateCaseIds(NormalizedEntry.FocusCaseIds);
|
||||
if (NormalizedEntry.HistoryEntryId.IsEmpty())
|
||||
{
|
||||
NormalizedEntry.HistoryEntryId = MakeCoachSessionQueueHistoryEntryId(NormalizedEntry);
|
||||
}
|
||||
return NormalizedEntry;
|
||||
}
|
||||
|
||||
FHyperTwistTrainingCoachSessionQueueHistoryEntry BuildCoachSessionQueueHistoryEntry(
|
||||
const FHyperTwistTrainingCoachSessionQueueState& QueueState,
|
||||
const FHyperTwistTrainingCoachSessionQueueStateEntry& CurrentEntry,
|
||||
const FHyperTwistTrainingCoachSessionQueueStateEntry* PreviousEntry
|
||||
)
|
||||
{
|
||||
FHyperTwistTrainingCoachSessionQueueHistoryEntry HistoryEntry;
|
||||
HistoryEntry.QueueId = QueueState.QueueId;
|
||||
HistoryEntry.UserId = QueueState.UserId;
|
||||
HistoryEntry.EntryId = CurrentEntry.EntryId;
|
||||
HistoryEntry.SourceLabel = CurrentEntry.SourceLabel;
|
||||
HistoryEntry.WorkFingerprint = CurrentEntry.WorkFingerprint;
|
||||
HistoryEntry.EventKind = DeriveCoachSessionQueueEventKind(PreviousEntry, CurrentEntry);
|
||||
HistoryEntry.EventAtUtc = ResolveCoachSessionQueueHistoryEventAtUtc(
|
||||
QueueState,
|
||||
CurrentEntry,
|
||||
HistoryEntry.EventKind
|
||||
);
|
||||
HistoryEntry.PreviousEntryState = PreviousEntry != nullptr
|
||||
? PreviousEntry->EntryState
|
||||
: CurrentEntry.EntryState;
|
||||
HistoryEntry.NewEntryState = CurrentEntry.EntryState;
|
||||
HistoryEntry.SourceTrainingSessionId = CurrentEntry.SourceTrainingSessionId;
|
||||
HistoryEntry.DeferredUntilUtc = CurrentEntry.DeferredUntilUtc;
|
||||
HistoryEntry.DeferralReasonLabel = CurrentEntry.DeferralReasonLabel;
|
||||
HistoryEntry.FocusDeckId = CurrentEntry.FocusDeckId;
|
||||
HistoryEntry.FocusMethodSegmentId = CurrentEntry.FocusMethodSegmentId;
|
||||
HistoryEntry.FocusCaseIds = CurrentEntry.FocusCaseIds;
|
||||
HistoryEntry.bRequiresFollowUp = CurrentEntry.bRequiresFollowUp;
|
||||
HistoryEntry.bRequiresArchiveRetentionTuning = CurrentEntry.bRequiresArchiveRetentionTuning;
|
||||
HistoryEntry.bWasManualReschedule = CurrentEntry.bWasManuallyRescheduled;
|
||||
return NormalizeCoachSessionQueueHistoryEntry(HistoryEntry);
|
||||
}
|
||||
|
||||
FHyperTwistTrainingCaseLearnerState DeriveLearnerStateFromReviewHistory(
|
||||
const FString& UserId,
|
||||
const FString& DeckId,
|
||||
|
|
@ -2932,6 +3073,26 @@ FHyperTwistTrainingRepositoryIntegrityReport UHyperTwistTrainingRepositoryLibrar
|
|||
SeenCoachSessionQueueKeys.Add(QueueKey);
|
||||
}
|
||||
|
||||
Report.TotalCoachSessionQueueHistoryCount = RepositoryState.StoredCoachSessionQueueHistory.Num();
|
||||
TSet<FString> SeenCoachSessionQueueHistoryIds;
|
||||
for (const FHyperTwistTrainingCoachSessionQueueHistoryEntry& HistoryEntry : RepositoryState.StoredCoachSessionQueueHistory)
|
||||
{
|
||||
const FHyperTwistTrainingCoachSessionQueueHistoryEntry NormalizedHistoryEntry =
|
||||
HyperTwistTrainingRepositoryLibraryInternal::NormalizeCoachSessionQueueHistoryEntry(HistoryEntry);
|
||||
if (!NormalizedHistoryEntry.IsStructurallyValid())
|
||||
{
|
||||
Report.InvalidCoachSessionQueueHistoryCount += 1;
|
||||
continue;
|
||||
}
|
||||
if (SeenCoachSessionQueueHistoryIds.Contains(NormalizedHistoryEntry.HistoryEntryId))
|
||||
{
|
||||
Report.DuplicateCoachSessionQueueHistoryCount += 1;
|
||||
continue;
|
||||
}
|
||||
|
||||
SeenCoachSessionQueueHistoryIds.Add(NormalizedHistoryEntry.HistoryEntryId);
|
||||
}
|
||||
|
||||
if (Report.InvalidRunRecordCount > 0)
|
||||
{
|
||||
HyperTwistTrainingRepositoryLibraryInternal::AppendRepairAction(
|
||||
|
|
@ -3047,6 +3208,20 @@ FHyperTwistTrainingRepositoryIntegrityReport UHyperTwistTrainingRepositoryLibrar
|
|||
TEXT("normalize-and-deduplicate-coach-session-queues")
|
||||
);
|
||||
}
|
||||
if (Report.InvalidCoachSessionQueueHistoryCount > 0)
|
||||
{
|
||||
HyperTwistTrainingRepositoryLibraryInternal::AppendRepairAction(
|
||||
Report.RepairActions,
|
||||
TEXT("drop-invalid-coach-session-queue-history")
|
||||
);
|
||||
}
|
||||
if (Report.DuplicateCoachSessionQueueHistoryCount > 0)
|
||||
{
|
||||
HyperTwistTrainingRepositoryLibraryInternal::AppendRepairAction(
|
||||
Report.RepairActions,
|
||||
TEXT("normalize-and-deduplicate-coach-session-queue-history")
|
||||
);
|
||||
}
|
||||
|
||||
return Report;
|
||||
}
|
||||
|
|
@ -3409,6 +3584,55 @@ FHyperTwistTrainingRepositoryState UHyperTwistTrainingRepositoryLibrary::RepairR
|
|||
return Left.QueueId < Right.QueueId;
|
||||
});
|
||||
|
||||
TMap<FString, int32> CoachSessionQueueHistoryIndexById;
|
||||
for (const FHyperTwistTrainingCoachSessionQueueHistoryEntry& HistoryEntry : RepositoryState.StoredCoachSessionQueueHistory)
|
||||
{
|
||||
const FHyperTwistTrainingCoachSessionQueueHistoryEntry NormalizedHistoryEntry =
|
||||
HyperTwistTrainingRepositoryLibraryInternal::NormalizeCoachSessionQueueHistoryEntry(HistoryEntry);
|
||||
if (!NormalizedHistoryEntry.IsStructurallyValid())
|
||||
{
|
||||
continue;
|
||||
}
|
||||
|
||||
if (const int32* ExistingIndex = CoachSessionQueueHistoryIndexById.Find(NormalizedHistoryEntry.HistoryEntryId))
|
||||
{
|
||||
RepairedState.StoredCoachSessionQueueHistory[*ExistingIndex] = NormalizedHistoryEntry;
|
||||
}
|
||||
else
|
||||
{
|
||||
CoachSessionQueueHistoryIndexById.Add(
|
||||
NormalizedHistoryEntry.HistoryEntryId,
|
||||
RepairedState.StoredCoachSessionQueueHistory.Add(NormalizedHistoryEntry)
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
RepairedState.StoredCoachSessionQueueHistory.Sort([](
|
||||
const FHyperTwistTrainingCoachSessionQueueHistoryEntry& Left,
|
||||
const FHyperTwistTrainingCoachSessionQueueHistoryEntry& Right
|
||||
)
|
||||
{
|
||||
if (Left.UserId != Right.UserId)
|
||||
{
|
||||
return Left.UserId < Right.UserId;
|
||||
}
|
||||
if (Left.QueueId != Right.QueueId)
|
||||
{
|
||||
return Left.QueueId < Right.QueueId;
|
||||
}
|
||||
|
||||
const int32 UtcCompare = HyperTwistTrainingRepositoryLibraryInternal::CompareUtcStrings(
|
||||
Left.EventAtUtc,
|
||||
Right.EventAtUtc
|
||||
);
|
||||
if (UtcCompare != 0)
|
||||
{
|
||||
return UtcCompare < 0;
|
||||
}
|
||||
|
||||
return Left.HistoryEntryId < Right.HistoryEntryId;
|
||||
});
|
||||
|
||||
return RepairedState;
|
||||
}
|
||||
|
||||
|
|
@ -5657,6 +5881,47 @@ TArray<FHyperTwistTrainingCoachSessionQueueState> UHyperTwistTrainingRepositoryL
|
|||
return QueueStates;
|
||||
}
|
||||
|
||||
TArray<FHyperTwistTrainingCoachSessionQueueHistoryEntry> UHyperTwistTrainingRepositoryLibrary::ListCoachSessionQueueHistoryEntries(
|
||||
const FHyperTwistTrainingRepositoryState& RepositoryState,
|
||||
const FString& UserId,
|
||||
const FString& QueueId
|
||||
)
|
||||
{
|
||||
TArray<FHyperTwistTrainingCoachSessionQueueHistoryEntry> HistoryEntries;
|
||||
if (UserId.IsEmpty() || QueueId.IsEmpty())
|
||||
{
|
||||
return HistoryEntries;
|
||||
}
|
||||
|
||||
for (const FHyperTwistTrainingCoachSessionQueueHistoryEntry& HistoryEntry : RepositoryState.StoredCoachSessionQueueHistory)
|
||||
{
|
||||
if (HistoryEntry.UserId == UserId
|
||||
&& HistoryEntry.QueueId == QueueId
|
||||
&& HistoryEntry.IsStructurallyValid())
|
||||
{
|
||||
HistoryEntries.Add(HistoryEntry);
|
||||
}
|
||||
}
|
||||
|
||||
HistoryEntries.Sort([](
|
||||
const FHyperTwistTrainingCoachSessionQueueHistoryEntry& Left,
|
||||
const FHyperTwistTrainingCoachSessionQueueHistoryEntry& Right
|
||||
)
|
||||
{
|
||||
const int32 UtcCompare = HyperTwistTrainingRepositoryLibraryInternal::CompareUtcStrings(
|
||||
Left.EventAtUtc,
|
||||
Right.EventAtUtc
|
||||
);
|
||||
if (UtcCompare != 0)
|
||||
{
|
||||
return UtcCompare < 0;
|
||||
}
|
||||
|
||||
return Left.HistoryEntryId < Right.HistoryEntryId;
|
||||
});
|
||||
return HistoryEntries;
|
||||
}
|
||||
|
||||
bool UHyperTwistTrainingRepositoryLibrary::TryGetCoachSessionQueueState(
|
||||
const FHyperTwistTrainingRepositoryState& RepositoryState,
|
||||
const FString& UserId,
|
||||
|
|
@ -5701,7 +5966,7 @@ FHyperTwistTrainingRepositoryState UHyperTwistTrainingRepositoryLibrary::UpsertC
|
|||
return UpdatedRepositoryState;
|
||||
}
|
||||
|
||||
FHyperTwistTrainingCoachSessionQueueState* ExistingQueueState =
|
||||
const FHyperTwistTrainingCoachSessionQueueState* ExistingQueueState =
|
||||
UpdatedRepositoryState.StoredCoachSessionQueues.FindByPredicate(
|
||||
[&NormalizedQueueState](const FHyperTwistTrainingCoachSessionQueueState& Candidate)
|
||||
{
|
||||
|
|
@ -5709,9 +5974,65 @@ FHyperTwistTrainingRepositoryState UHyperTwistTrainingRepositoryLibrary::UpsertC
|
|||
&& Candidate.QueueId == NormalizedQueueState.QueueId;
|
||||
}
|
||||
);
|
||||
if (ExistingQueueState != nullptr)
|
||||
|
||||
for (const FHyperTwistTrainingCoachSessionQueueStateEntry& CurrentEntry : NormalizedQueueState.Entries)
|
||||
{
|
||||
*ExistingQueueState = NormalizedQueueState;
|
||||
const FHyperTwistTrainingCoachSessionQueueStateEntry* PreviousEntry = ExistingQueueState != nullptr
|
||||
? ExistingQueueState->Entries.FindByPredicate(
|
||||
[&CurrentEntry](const FHyperTwistTrainingCoachSessionQueueStateEntry& Candidate)
|
||||
{
|
||||
return Candidate.EntryId == CurrentEntry.EntryId;
|
||||
}
|
||||
)
|
||||
: nullptr;
|
||||
if (PreviousEntry != nullptr && PreviousEntry->WorkFingerprint != CurrentEntry.WorkFingerprint)
|
||||
{
|
||||
PreviousEntry = nullptr;
|
||||
}
|
||||
if (PreviousEntry != nullptr
|
||||
&& !HyperTwistTrainingRepositoryLibraryInternal::DidCoachSessionQueueEntryChange(
|
||||
*PreviousEntry,
|
||||
CurrentEntry
|
||||
))
|
||||
{
|
||||
continue;
|
||||
}
|
||||
|
||||
const FHyperTwistTrainingCoachSessionQueueHistoryEntry HistoryEntry =
|
||||
HyperTwistTrainingRepositoryLibraryInternal::BuildCoachSessionQueueHistoryEntry(
|
||||
NormalizedQueueState,
|
||||
CurrentEntry,
|
||||
PreviousEntry
|
||||
);
|
||||
if (!HistoryEntry.IsStructurallyValid())
|
||||
{
|
||||
continue;
|
||||
}
|
||||
|
||||
const bool bAlreadyExists =
|
||||
UpdatedRepositoryState.StoredCoachSessionQueueHistory.ContainsByPredicate(
|
||||
[&HistoryEntry](const FHyperTwistTrainingCoachSessionQueueHistoryEntry& Candidate)
|
||||
{
|
||||
return Candidate.HistoryEntryId == HistoryEntry.HistoryEntryId;
|
||||
}
|
||||
);
|
||||
if (!bAlreadyExists)
|
||||
{
|
||||
UpdatedRepositoryState.StoredCoachSessionQueueHistory.Add(HistoryEntry);
|
||||
}
|
||||
}
|
||||
|
||||
FHyperTwistTrainingCoachSessionQueueState* MutableExistingQueueState =
|
||||
UpdatedRepositoryState.StoredCoachSessionQueues.FindByPredicate(
|
||||
[&NormalizedQueueState](const FHyperTwistTrainingCoachSessionQueueState& Candidate)
|
||||
{
|
||||
return Candidate.UserId == NormalizedQueueState.UserId
|
||||
&& Candidate.QueueId == NormalizedQueueState.QueueId;
|
||||
}
|
||||
);
|
||||
if (MutableExistingQueueState != nullptr)
|
||||
{
|
||||
*MutableExistingQueueState = NormalizedQueueState;
|
||||
}
|
||||
else
|
||||
{
|
||||
|
|
@ -5721,6 +6042,202 @@ FHyperTwistTrainingRepositoryState UHyperTwistTrainingRepositoryLibrary::UpsertC
|
|||
return UpdatedRepositoryState;
|
||||
}
|
||||
|
||||
FHyperTwistTrainingCoachSessionQueueExecutionSummary UHyperTwistTrainingRepositoryLibrary::DeriveCoachSessionQueueExecutionSummary(
|
||||
const FHyperTwistTrainingRepositoryState& RepositoryState,
|
||||
const FString& UserId,
|
||||
const FString& QueueId,
|
||||
const FString& ReferenceUtc
|
||||
)
|
||||
{
|
||||
FHyperTwistTrainingCoachSessionQueueExecutionSummary Summary;
|
||||
if (UserId.IsEmpty() || QueueId.IsEmpty())
|
||||
{
|
||||
return Summary;
|
||||
}
|
||||
|
||||
Summary.UserId = UserId;
|
||||
Summary.QueueId = QueueId;
|
||||
Summary.ReferenceUtc = HyperTwistTrainingRepositoryLibraryInternal::ResolveReferenceUtc(ReferenceUtc);
|
||||
|
||||
FHyperTwistTrainingCoachSessionQueueState QueueState;
|
||||
if (TryGetCoachSessionQueueState(RepositoryState, UserId, QueueId, QueueState))
|
||||
{
|
||||
Summary.FocusDeckId = QueueState.FocusDeckId;
|
||||
Summary.FocusMethodSegmentId = QueueState.FocusMethodSegmentId;
|
||||
Summary.bHasOutstandingDeferredWork =
|
||||
QueueState.DeferredEntryCount > 0 || QueueState.PendingEntryCount > 0;
|
||||
}
|
||||
|
||||
const TArray<FHyperTwistTrainingCoachSessionQueueHistoryEntry> HistoryEntries =
|
||||
ListCoachSessionQueueHistoryEntries(RepositoryState, UserId, QueueId);
|
||||
if (HistoryEntries.Num() == 0)
|
||||
{
|
||||
return Summary;
|
||||
}
|
||||
|
||||
Summary.TotalHistoryEntryCount = HistoryEntries.Num();
|
||||
TSet<FString> UniqueEntryIds;
|
||||
TMap<FString, int32> DeferredCountsByEntryId;
|
||||
TMap<FString, int32> CompletedCountsByEntryId;
|
||||
TMap<FString, int32> DeferralReasonCounts;
|
||||
|
||||
for (const FHyperTwistTrainingCoachSessionQueueHistoryEntry& HistoryEntry : HistoryEntries)
|
||||
{
|
||||
if (!HistoryEntry.IsStructurallyValid())
|
||||
{
|
||||
continue;
|
||||
}
|
||||
|
||||
UniqueEntryIds.Add(HistoryEntry.EntryId);
|
||||
if (Summary.FocusDeckId.IsEmpty())
|
||||
{
|
||||
Summary.FocusDeckId = HistoryEntry.FocusDeckId;
|
||||
}
|
||||
if (Summary.FocusMethodSegmentId.IsEmpty())
|
||||
{
|
||||
Summary.FocusMethodSegmentId = HistoryEntry.FocusMethodSegmentId;
|
||||
}
|
||||
|
||||
if (HyperTwistTrainingRepositoryLibraryInternal::CompareUtcStrings(
|
||||
HistoryEntry.EventAtUtc,
|
||||
Summary.ReferenceUtc
|
||||
) > 0)
|
||||
{
|
||||
continue;
|
||||
}
|
||||
|
||||
switch (HistoryEntry.EventKind)
|
||||
{
|
||||
case EHyperTwistTrainingCoachSessionQueueEventKind::Queued:
|
||||
Summary.QueuedEventCount += 1;
|
||||
break;
|
||||
case EHyperTwistTrainingCoachSessionQueueEventKind::Ready:
|
||||
Summary.ReadyEventCount += 1;
|
||||
break;
|
||||
case EHyperTwistTrainingCoachSessionQueueEventKind::Started:
|
||||
Summary.StartedEventCount += 1;
|
||||
if (Summary.LastStartedAtUtc.IsEmpty()
|
||||
|| HyperTwistTrainingRepositoryLibraryInternal::CompareUtcStrings(
|
||||
HistoryEntry.EventAtUtc,
|
||||
Summary.LastStartedAtUtc
|
||||
) > 0)
|
||||
{
|
||||
Summary.LastStartedAtUtc = HistoryEntry.EventAtUtc;
|
||||
}
|
||||
break;
|
||||
case EHyperTwistTrainingCoachSessionQueueEventKind::Deferred:
|
||||
Summary.DeferredEventCount += 1;
|
||||
DeferredCountsByEntryId.FindOrAdd(HistoryEntry.EntryId) += 1;
|
||||
if (!HistoryEntry.DeferralReasonLabel.IsEmpty())
|
||||
{
|
||||
DeferralReasonCounts.FindOrAdd(HistoryEntry.DeferralReasonLabel) += 1;
|
||||
}
|
||||
if (Summary.LastDeferredAtUtc.IsEmpty()
|
||||
|| HyperTwistTrainingRepositoryLibraryInternal::CompareUtcStrings(
|
||||
HistoryEntry.EventAtUtc,
|
||||
Summary.LastDeferredAtUtc
|
||||
) > 0)
|
||||
{
|
||||
Summary.LastDeferredAtUtc = HistoryEntry.EventAtUtc;
|
||||
}
|
||||
break;
|
||||
case EHyperTwistTrainingCoachSessionQueueEventKind::Promoted:
|
||||
Summary.PromotedEventCount += 1;
|
||||
break;
|
||||
case EHyperTwistTrainingCoachSessionQueueEventKind::Completed:
|
||||
Summary.CompletedEventCount += 1;
|
||||
CompletedCountsByEntryId.FindOrAdd(HistoryEntry.EntryId) += 1;
|
||||
if (Summary.LastCompletedAtUtc.IsEmpty()
|
||||
|| HyperTwistTrainingRepositoryLibraryInternal::CompareUtcStrings(
|
||||
HistoryEntry.EventAtUtc,
|
||||
Summary.LastCompletedAtUtc
|
||||
) > 0)
|
||||
{
|
||||
Summary.LastCompletedAtUtc = HistoryEntry.EventAtUtc;
|
||||
}
|
||||
break;
|
||||
case EHyperTwistTrainingCoachSessionQueueEventKind::Skipped:
|
||||
Summary.SkippedEventCount += 1;
|
||||
break;
|
||||
case EHyperTwistTrainingCoachSessionQueueEventKind::Expired:
|
||||
Summary.ExpiredEventCount += 1;
|
||||
break;
|
||||
default:
|
||||
break;
|
||||
}
|
||||
|
||||
if (HistoryEntry.bWasManualReschedule)
|
||||
{
|
||||
Summary.ManualRescheduleEventCount += 1;
|
||||
}
|
||||
if (HistoryEntry.bRequiresArchiveRetentionTuning)
|
||||
{
|
||||
Summary.ArchiveAwareEventCount += 1;
|
||||
}
|
||||
if (HistoryEntry.bRequiresFollowUp
|
||||
&& HistoryEntry.EventKind == EHyperTwistTrainingCoachSessionQueueEventKind::Deferred)
|
||||
{
|
||||
Summary.BlockedFollowUpEventCount += 1;
|
||||
}
|
||||
}
|
||||
|
||||
Summary.UniqueEntryCount = UniqueEntryIds.Num();
|
||||
|
||||
int32 DominantDeferredCount = 0;
|
||||
for (const TPair<FString, int32>& Pair : DeferredCountsByEntryId)
|
||||
{
|
||||
if (Pair.Value > DominantDeferredCount
|
||||
|| (Pair.Value == DominantDeferredCount
|
||||
&& (Summary.MostDeferredEntryId.IsEmpty() || Pair.Key < Summary.MostDeferredEntryId)))
|
||||
{
|
||||
DominantDeferredCount = Pair.Value;
|
||||
Summary.MostDeferredEntryId = Pair.Key;
|
||||
}
|
||||
}
|
||||
|
||||
int32 DominantCompletedCount = 0;
|
||||
for (const TPair<FString, int32>& Pair : CompletedCountsByEntryId)
|
||||
{
|
||||
if (Pair.Value > DominantCompletedCount
|
||||
|| (Pair.Value == DominantCompletedCount
|
||||
&& (Summary.MostCompletedEntryId.IsEmpty() || Pair.Key < Summary.MostCompletedEntryId)))
|
||||
{
|
||||
DominantCompletedCount = Pair.Value;
|
||||
Summary.MostCompletedEntryId = Pair.Key;
|
||||
}
|
||||
}
|
||||
|
||||
int32 DominantReasonCount = 0;
|
||||
for (const TPair<FString, int32>& Pair : DeferralReasonCounts)
|
||||
{
|
||||
if (Pair.Value > DominantReasonCount
|
||||
|| (Pair.Value == DominantReasonCount
|
||||
&& (Summary.DominantDeferralReasonLabel.IsEmpty() || Pair.Key < Summary.DominantDeferralReasonLabel)))
|
||||
{
|
||||
DominantReasonCount = Pair.Value;
|
||||
Summary.DominantDeferralReasonLabel = Pair.Key;
|
||||
}
|
||||
}
|
||||
|
||||
const int32 TerminalEventCount =
|
||||
Summary.CompletedEventCount + Summary.SkippedEventCount + Summary.ExpiredEventCount;
|
||||
Summary.bHasCompletionGap = Summary.StartedEventCount > TerminalEventCount;
|
||||
Summary.bHasArchiveAwareFriction = Summary.ArchiveAwareEventCount > 0;
|
||||
Summary.bHasBlockedFollowUpFriction = Summary.BlockedFollowUpEventCount > 0;
|
||||
Summary.bHasRepeatManualRescheduleFriction = Summary.ManualRescheduleEventCount >= 2;
|
||||
Summary.ExecutionPressureScore = FMath::Clamp(
|
||||
static_cast<float>(Summary.DeferredEventCount) * 7.0f
|
||||
+ static_cast<float>(Summary.ManualRescheduleEventCount) * 10.0f
|
||||
+ static_cast<float>(Summary.ArchiveAwareEventCount) * 12.0f
|
||||
+ static_cast<float>(Summary.BlockedFollowUpEventCount) * 12.0f
|
||||
+ (Summary.bHasCompletionGap ? 10.0f : 0.0f),
|
||||
0.0f,
|
||||
100.0f
|
||||
);
|
||||
|
||||
return Summary;
|
||||
}
|
||||
|
||||
FHyperTwistTrainingCoachActionPlanSummary UHyperTwistTrainingRepositoryLibrary::DeriveCoachActionPlanSummary(
|
||||
const FHyperTwistTrainingRepositoryState& RepositoryState,
|
||||
const FString& UserId,
|
||||
|
|
|
|||
|
|
@ -405,6 +405,18 @@ FHyperTwistCoachSchedulePolicySummary UHyperTwistTrainingRuntimeLibrary::GetActi
|
|||
return FHyperTwistCoachSchedulePolicySummary();
|
||||
}
|
||||
|
||||
FHyperTwistTrainingCoachSessionQueueExecutionSummary UHyperTwistTrainingRuntimeLibrary::GetActiveTrainingCoachSessionQueueExecutionSummary(
|
||||
UObject* WorldContextObject
|
||||
)
|
||||
{
|
||||
if (UHyperTwistTrainingSubsystem* TrainingSubsystem = GetTrainingSubsystem(WorldContextObject))
|
||||
{
|
||||
return TrainingSubsystem->GetActiveCoachSessionQueueExecutionSummary();
|
||||
}
|
||||
|
||||
return FHyperTwistTrainingCoachSessionQueueExecutionSummary();
|
||||
}
|
||||
|
||||
TArray<FHyperTwistTrainingMethodSegment> UHyperTwistTrainingRuntimeLibrary::GetActiveTrainingLearnerMethodSegments(UObject* WorldContextObject)
|
||||
{
|
||||
if (UHyperTwistTrainingSubsystem* TrainingSubsystem = GetTrainingSubsystem(WorldContextObject))
|
||||
|
|
|
|||
|
|
@ -183,6 +183,11 @@ FHyperTwistCoachSchedulePolicySummary UHyperTwistTrainingSubsystem::GetActiveCoa
|
|||
return ActiveCoachSchedulePolicySummary;
|
||||
}
|
||||
|
||||
FHyperTwistTrainingCoachSessionQueueExecutionSummary UHyperTwistTrainingSubsystem::GetActiveCoachSessionQueueExecutionSummary() const
|
||||
{
|
||||
return ActiveCoachSessionQueueExecutionSummary;
|
||||
}
|
||||
|
||||
TArray<FHyperTwistTrainingMethodSegment> UHyperTwistTrainingSubsystem::GetActiveLearnerMethodSegments() const
|
||||
{
|
||||
return ActiveLearnerMethodSegments;
|
||||
|
|
@ -1385,6 +1390,7 @@ void UHyperTwistTrainingSubsystem::RefreshRepositoryViews()
|
|||
ActiveCoachSessionQueueState = FHyperTwistTrainingCoachSessionQueueState();
|
||||
ActiveCoachScheduleHorizonSummary = FHyperTwistCoachScheduleHorizonSummary();
|
||||
ActiveCoachSchedulePolicySummary = FHyperTwistCoachSchedulePolicySummary();
|
||||
ActiveCoachSessionQueueExecutionSummary = FHyperTwistTrainingCoachSessionQueueExecutionSummary();
|
||||
ActiveLearnerMethodSegments.Reset();
|
||||
ActiveLearnerPresets.Reset();
|
||||
ActiveTrainingSessionTemplates.Reset();
|
||||
|
|
@ -1598,12 +1604,20 @@ void UHyperTwistTrainingSubsystem::RefreshRepositoryViews()
|
|||
ActiveCoachArchivePolicySummary,
|
||||
ActiveLearnerDeckStateSummary.ReferenceUtc
|
||||
);
|
||||
ActiveCoachSessionQueueExecutionSummary =
|
||||
UHyperTwistTrainingRepositoryLibrary::DeriveCoachSessionQueueExecutionSummary(
|
||||
TrainingRepositoryState,
|
||||
ActiveCoachSessionQueueState.UserId,
|
||||
ActiveCoachSessionQueueState.QueueId,
|
||||
ActiveLearnerDeckStateSummary.ReferenceUtc
|
||||
);
|
||||
}
|
||||
else
|
||||
{
|
||||
ActiveCoachSessionQueueState = FHyperTwistTrainingCoachSessionQueueState();
|
||||
ActiveCoachScheduleHorizonSummary = FHyperTwistCoachScheduleHorizonSummary();
|
||||
ActiveCoachSchedulePolicySummary = FHyperTwistCoachSchedulePolicySummary();
|
||||
ActiveCoachSessionQueueExecutionSummary = FHyperTwistTrainingCoachSessionQueueExecutionSummary();
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
|||
|
|
@ -218,6 +218,9 @@ public:
|
|||
UFUNCTION(BlueprintPure, Category = "HyperTwist|Training|Coach")
|
||||
static FHyperTwistCoachSchedulePolicySummary MakeSampleTrainingCoachSchedulePolicySummary();
|
||||
|
||||
UFUNCTION(BlueprintPure, Category = "HyperTwist|Training|Coach")
|
||||
static FHyperTwistTrainingCoachSessionQueueExecutionSummary MakeSampleTrainingCoachSessionQueueExecutionSummary();
|
||||
|
||||
UFUNCTION(BlueprintPure, Category = "HyperTwist|Validation")
|
||||
static bool IsPuzzleStateStructurallyValid(const FHyperTwistPuzzleState& State);
|
||||
|
||||
|
|
|
|||
|
|
@ -400,6 +400,13 @@ public:
|
|||
const FString& UserId
|
||||
);
|
||||
|
||||
UFUNCTION(BlueprintPure, Category = "HyperTwist|Training|Repository|Coach")
|
||||
static TArray<FHyperTwistTrainingCoachSessionQueueHistoryEntry> ListCoachSessionQueueHistoryEntries(
|
||||
const FHyperTwistTrainingRepositoryState& RepositoryState,
|
||||
const FString& UserId,
|
||||
const FString& QueueId
|
||||
);
|
||||
|
||||
UFUNCTION(BlueprintCallable, Category = "HyperTwist|Training|Repository|Coach")
|
||||
static bool TryGetCoachSessionQueueState(
|
||||
const FHyperTwistTrainingRepositoryState& RepositoryState,
|
||||
|
|
@ -414,6 +421,14 @@ public:
|
|||
const FHyperTwistTrainingCoachSessionQueueState& QueueState
|
||||
);
|
||||
|
||||
UFUNCTION(BlueprintPure, Category = "HyperTwist|Training|Repository|Coach")
|
||||
static FHyperTwistTrainingCoachSessionQueueExecutionSummary DeriveCoachSessionQueueExecutionSummary(
|
||||
const FHyperTwistTrainingRepositoryState& RepositoryState,
|
||||
const FString& UserId,
|
||||
const FString& QueueId,
|
||||
const FString& ReferenceUtc
|
||||
);
|
||||
|
||||
UFUNCTION(BlueprintPure, Category = "HyperTwist|Training|Repository|Coach")
|
||||
static FHyperTwistTrainingCoachActionPlanSummary DeriveCoachActionPlanSummary(
|
||||
const FHyperTwistTrainingRepositoryState& RepositoryState,
|
||||
|
|
|
|||
|
|
@ -135,6 +135,11 @@ public:
|
|||
UObject* WorldContextObject
|
||||
);
|
||||
|
||||
UFUNCTION(BlueprintPure, Category = "HyperTwist|Training|Coach", meta = (WorldContext = "WorldContextObject"))
|
||||
static FHyperTwistTrainingCoachSessionQueueExecutionSummary GetActiveTrainingCoachSessionQueueExecutionSummary(
|
||||
UObject* WorldContextObject
|
||||
);
|
||||
|
||||
UFUNCTION(BlueprintPure, Category = "HyperTwist|Training", meta = (WorldContext = "WorldContextObject"))
|
||||
static TArray<FHyperTwistTrainingMethodSegment> GetActiveTrainingLearnerMethodSegments(UObject* WorldContextObject);
|
||||
|
||||
|
|
|
|||
|
|
@ -116,6 +116,9 @@ public:
|
|||
UFUNCTION(BlueprintPure, Category = "HyperTwist|Training|Coach")
|
||||
FHyperTwistCoachSchedulePolicySummary GetActiveCoachSchedulePolicySummary() const;
|
||||
|
||||
UFUNCTION(BlueprintPure, Category = "HyperTwist|Training|Coach")
|
||||
FHyperTwistTrainingCoachSessionQueueExecutionSummary GetActiveCoachSessionQueueExecutionSummary() const;
|
||||
|
||||
UFUNCTION(BlueprintPure, Category = "HyperTwist|Training")
|
||||
TArray<FHyperTwistTrainingMethodSegment> GetActiveLearnerMethodSegments() const;
|
||||
|
||||
|
|
@ -498,6 +501,9 @@ private:
|
|||
UPROPERTY()
|
||||
FHyperTwistCoachSchedulePolicySummary ActiveCoachSchedulePolicySummary;
|
||||
|
||||
UPROPERTY()
|
||||
FHyperTwistTrainingCoachSessionQueueExecutionSummary ActiveCoachSessionQueueExecutionSummary;
|
||||
|
||||
UPROPERTY()
|
||||
TArray<FHyperTwistTrainingMethodSegment> ActiveLearnerMethodSegments;
|
||||
|
||||
|
|
|
|||
|
|
@ -2730,6 +2730,19 @@ enum class EHyperTwistTrainingCoachSessionQueueEntryState : uint8
|
|||
Expired UMETA(DisplayName = "Expired")
|
||||
};
|
||||
|
||||
UENUM(BlueprintType)
|
||||
enum class EHyperTwistTrainingCoachSessionQueueEventKind : uint8
|
||||
{
|
||||
Queued UMETA(DisplayName = "Queued"),
|
||||
Ready UMETA(DisplayName = "Ready"),
|
||||
Started UMETA(DisplayName = "Started"),
|
||||
Deferred UMETA(DisplayName = "Deferred"),
|
||||
Promoted UMETA(DisplayName = "Promoted"),
|
||||
Completed UMETA(DisplayName = "Completed"),
|
||||
Skipped UMETA(DisplayName = "Skipped"),
|
||||
Expired UMETA(DisplayName = "Expired")
|
||||
};
|
||||
|
||||
USTRUCT(BlueprintType)
|
||||
struct FHyperTwistTrainingCoachActionPlanEntry
|
||||
{
|
||||
|
|
@ -3035,6 +3048,182 @@ struct FHyperTwistTrainingCoachSessionQueueState
|
|||
}
|
||||
};
|
||||
|
||||
USTRUCT(BlueprintType)
|
||||
struct FHyperTwistTrainingCoachSessionQueueHistoryEntry
|
||||
{
|
||||
GENERATED_BODY()
|
||||
|
||||
UPROPERTY(EditAnywhere, BlueprintReadWrite, Category = "HyperTwist")
|
||||
FString HistoryEntryId;
|
||||
|
||||
UPROPERTY(EditAnywhere, BlueprintReadWrite, Category = "HyperTwist")
|
||||
FString QueueId;
|
||||
|
||||
UPROPERTY(EditAnywhere, BlueprintReadWrite, Category = "HyperTwist")
|
||||
FString UserId;
|
||||
|
||||
UPROPERTY(EditAnywhere, BlueprintReadWrite, Category = "HyperTwist")
|
||||
FString EntryId;
|
||||
|
||||
UPROPERTY(EditAnywhere, BlueprintReadWrite, Category = "HyperTwist")
|
||||
FString SourceLabel;
|
||||
|
||||
UPROPERTY(EditAnywhere, BlueprintReadWrite, Category = "HyperTwist")
|
||||
FString WorkFingerprint;
|
||||
|
||||
UPROPERTY(EditAnywhere, BlueprintReadWrite, Category = "HyperTwist")
|
||||
EHyperTwistTrainingCoachSessionQueueEventKind EventKind =
|
||||
EHyperTwistTrainingCoachSessionQueueEventKind::Queued;
|
||||
|
||||
UPROPERTY(EditAnywhere, BlueprintReadWrite, Category = "HyperTwist")
|
||||
FString EventAtUtc;
|
||||
|
||||
UPROPERTY(EditAnywhere, BlueprintReadWrite, Category = "HyperTwist")
|
||||
EHyperTwistTrainingCoachSessionQueueEntryState PreviousEntryState =
|
||||
EHyperTwistTrainingCoachSessionQueueEntryState::Pending;
|
||||
|
||||
UPROPERTY(EditAnywhere, BlueprintReadWrite, Category = "HyperTwist")
|
||||
EHyperTwistTrainingCoachSessionQueueEntryState NewEntryState =
|
||||
EHyperTwistTrainingCoachSessionQueueEntryState::Pending;
|
||||
|
||||
UPROPERTY(EditAnywhere, BlueprintReadWrite, Category = "HyperTwist")
|
||||
FString SourceTrainingSessionId;
|
||||
|
||||
UPROPERTY(EditAnywhere, BlueprintReadWrite, Category = "HyperTwist")
|
||||
FString DeferredUntilUtc;
|
||||
|
||||
UPROPERTY(EditAnywhere, BlueprintReadWrite, Category = "HyperTwist")
|
||||
FString DeferralReasonLabel;
|
||||
|
||||
UPROPERTY(EditAnywhere, BlueprintReadWrite, Category = "HyperTwist")
|
||||
FString FocusDeckId;
|
||||
|
||||
UPROPERTY(EditAnywhere, BlueprintReadWrite, Category = "HyperTwist")
|
||||
FString FocusMethodSegmentId;
|
||||
|
||||
UPROPERTY(EditAnywhere, BlueprintReadWrite, Category = "HyperTwist")
|
||||
TArray<FString> FocusCaseIds;
|
||||
|
||||
UPROPERTY(EditAnywhere, BlueprintReadWrite, Category = "HyperTwist")
|
||||
bool bRequiresFollowUp = false;
|
||||
|
||||
UPROPERTY(EditAnywhere, BlueprintReadWrite, Category = "HyperTwist")
|
||||
bool bRequiresArchiveRetentionTuning = false;
|
||||
|
||||
UPROPERTY(EditAnywhere, BlueprintReadWrite, Category = "HyperTwist")
|
||||
bool bWasManualReschedule = false;
|
||||
|
||||
bool IsStructurallyValid() const
|
||||
{
|
||||
return !HistoryEntryId.IsEmpty()
|
||||
&& !QueueId.IsEmpty()
|
||||
&& !UserId.IsEmpty()
|
||||
&& !EntryId.IsEmpty()
|
||||
&& !EventAtUtc.IsEmpty();
|
||||
}
|
||||
};
|
||||
|
||||
USTRUCT(BlueprintType)
|
||||
struct FHyperTwistTrainingCoachSessionQueueExecutionSummary
|
||||
{
|
||||
GENERATED_BODY()
|
||||
|
||||
UPROPERTY(EditAnywhere, BlueprintReadWrite, Category = "HyperTwist")
|
||||
FString UserId;
|
||||
|
||||
UPROPERTY(EditAnywhere, BlueprintReadWrite, Category = "HyperTwist")
|
||||
FString QueueId;
|
||||
|
||||
UPROPERTY(EditAnywhere, BlueprintReadWrite, Category = "HyperTwist")
|
||||
FString ReferenceUtc;
|
||||
|
||||
UPROPERTY(EditAnywhere, BlueprintReadWrite, Category = "HyperTwist")
|
||||
FString FocusDeckId;
|
||||
|
||||
UPROPERTY(EditAnywhere, BlueprintReadWrite, Category = "HyperTwist")
|
||||
FString FocusMethodSegmentId;
|
||||
|
||||
UPROPERTY(EditAnywhere, BlueprintReadWrite, Category = "HyperTwist")
|
||||
int32 TotalHistoryEntryCount = 0;
|
||||
|
||||
UPROPERTY(EditAnywhere, BlueprintReadWrite, Category = "HyperTwist")
|
||||
int32 UniqueEntryCount = 0;
|
||||
|
||||
UPROPERTY(EditAnywhere, BlueprintReadWrite, Category = "HyperTwist")
|
||||
int32 QueuedEventCount = 0;
|
||||
|
||||
UPROPERTY(EditAnywhere, BlueprintReadWrite, Category = "HyperTwist")
|
||||
int32 ReadyEventCount = 0;
|
||||
|
||||
UPROPERTY(EditAnywhere, BlueprintReadWrite, Category = "HyperTwist")
|
||||
int32 StartedEventCount = 0;
|
||||
|
||||
UPROPERTY(EditAnywhere, BlueprintReadWrite, Category = "HyperTwist")
|
||||
int32 DeferredEventCount = 0;
|
||||
|
||||
UPROPERTY(EditAnywhere, BlueprintReadWrite, Category = "HyperTwist")
|
||||
int32 PromotedEventCount = 0;
|
||||
|
||||
UPROPERTY(EditAnywhere, BlueprintReadWrite, Category = "HyperTwist")
|
||||
int32 CompletedEventCount = 0;
|
||||
|
||||
UPROPERTY(EditAnywhere, BlueprintReadWrite, Category = "HyperTwist")
|
||||
int32 SkippedEventCount = 0;
|
||||
|
||||
UPROPERTY(EditAnywhere, BlueprintReadWrite, Category = "HyperTwist")
|
||||
int32 ExpiredEventCount = 0;
|
||||
|
||||
UPROPERTY(EditAnywhere, BlueprintReadWrite, Category = "HyperTwist")
|
||||
int32 ManualRescheduleEventCount = 0;
|
||||
|
||||
UPROPERTY(EditAnywhere, BlueprintReadWrite, Category = "HyperTwist")
|
||||
int32 ArchiveAwareEventCount = 0;
|
||||
|
||||
UPROPERTY(EditAnywhere, BlueprintReadWrite, Category = "HyperTwist")
|
||||
int32 BlockedFollowUpEventCount = 0;
|
||||
|
||||
UPROPERTY(EditAnywhere, BlueprintReadWrite, Category = "HyperTwist")
|
||||
FString MostDeferredEntryId;
|
||||
|
||||
UPROPERTY(EditAnywhere, BlueprintReadWrite, Category = "HyperTwist")
|
||||
FString MostCompletedEntryId;
|
||||
|
||||
UPROPERTY(EditAnywhere, BlueprintReadWrite, Category = "HyperTwist")
|
||||
FString DominantDeferralReasonLabel;
|
||||
|
||||
UPROPERTY(EditAnywhere, BlueprintReadWrite, Category = "HyperTwist")
|
||||
FString LastStartedAtUtc;
|
||||
|
||||
UPROPERTY(EditAnywhere, BlueprintReadWrite, Category = "HyperTwist")
|
||||
FString LastDeferredAtUtc;
|
||||
|
||||
UPROPERTY(EditAnywhere, BlueprintReadWrite, Category = "HyperTwist")
|
||||
FString LastCompletedAtUtc;
|
||||
|
||||
UPROPERTY(EditAnywhere, BlueprintReadWrite, Category = "HyperTwist")
|
||||
float ExecutionPressureScore = 0.0f;
|
||||
|
||||
UPROPERTY(EditAnywhere, BlueprintReadWrite, Category = "HyperTwist")
|
||||
bool bHasOutstandingDeferredWork = false;
|
||||
|
||||
UPROPERTY(EditAnywhere, BlueprintReadWrite, Category = "HyperTwist")
|
||||
bool bHasCompletionGap = false;
|
||||
|
||||
UPROPERTY(EditAnywhere, BlueprintReadWrite, Category = "HyperTwist")
|
||||
bool bHasArchiveAwareFriction = false;
|
||||
|
||||
UPROPERTY(EditAnywhere, BlueprintReadWrite, Category = "HyperTwist")
|
||||
bool bHasBlockedFollowUpFriction = false;
|
||||
|
||||
UPROPERTY(EditAnywhere, BlueprintReadWrite, Category = "HyperTwist")
|
||||
bool bHasRepeatManualRescheduleFriction = false;
|
||||
|
||||
bool IsStructurallyValid() const
|
||||
{
|
||||
return !UserId.IsEmpty() && !QueueId.IsEmpty();
|
||||
}
|
||||
};
|
||||
|
||||
USTRUCT(BlueprintType)
|
||||
struct FHyperTwistTrainingCoachActionPlanSummary
|
||||
{
|
||||
|
|
@ -3703,6 +3892,9 @@ struct FHyperTwistTrainingRepositoryState
|
|||
|
||||
UPROPERTY(EditAnywhere, BlueprintReadWrite, Category = "HyperTwist")
|
||||
TArray<FHyperTwistTrainingCoachSessionQueueState> StoredCoachSessionQueues;
|
||||
|
||||
UPROPERTY(EditAnywhere, BlueprintReadWrite, Category = "HyperTwist")
|
||||
TArray<FHyperTwistTrainingCoachSessionQueueHistoryEntry> StoredCoachSessionQueueHistory;
|
||||
};
|
||||
|
||||
USTRUCT(BlueprintType)
|
||||
|
|
@ -3782,6 +3974,15 @@ struct FHyperTwistTrainingRepositoryIntegrityReport
|
|||
UPROPERTY(EditAnywhere, BlueprintReadWrite, Category = "HyperTwist")
|
||||
int32 DuplicateCoachSessionQueueCount = 0;
|
||||
|
||||
UPROPERTY(EditAnywhere, BlueprintReadWrite, Category = "HyperTwist")
|
||||
int32 TotalCoachSessionQueueHistoryCount = 0;
|
||||
|
||||
UPROPERTY(EditAnywhere, BlueprintReadWrite, Category = "HyperTwist")
|
||||
int32 InvalidCoachSessionQueueHistoryCount = 0;
|
||||
|
||||
UPROPERTY(EditAnywhere, BlueprintReadWrite, Category = "HyperTwist")
|
||||
int32 DuplicateCoachSessionQueueHistoryCount = 0;
|
||||
|
||||
UPROPERTY(EditAnywhere, BlueprintReadWrite, Category = "HyperTwist")
|
||||
TArray<FString> RepairActions;
|
||||
|
||||
|
|
@ -3802,6 +4003,8 @@ struct FHyperTwistTrainingRepositoryIntegrityReport
|
|||
|| InvalidCoachActionPlanCount > 0
|
||||
|| DuplicateCoachActionPlanCount > 0
|
||||
|| InvalidCoachSessionQueueCount > 0
|
||||
|| DuplicateCoachSessionQueueCount > 0;
|
||||
|| DuplicateCoachSessionQueueCount > 0
|
||||
|| InvalidCoachSessionQueueHistoryCount > 0
|
||||
|| DuplicateCoachSessionQueueHistoryCount > 0;
|
||||
}
|
||||
};
|
||||
|
|
|
|||
Loading…
Add table
Reference in a new issue