feat(insights): hit-rate breakdown by detector kind
Adds a "By detector kind" breakdown to the anomaly outcome report + Insights page, answering "which directional detector actually wins?". Only directional kinds (SuspensionFlip, SteamMove) resolve to hit/miss, so the breakdown naturally shows just those — mirrors the existing by-sport / by-severity bucketers and rendering. - AnomalyOutcomeReport.ByKind + EvaluateAnomalyOutcomesUseCase.BuildKindBuckets (keyed OutcomeBucketKeys.KindPrefix + enum name); threaded through the insights VM/service. - Insights page: new section + BucketRenderKind.Kind label case (resolves the enum name to the Anomaly.Kind.* resx); en/ru section heading. - 2 tests: empty-report ByKind + directional-kind grouping.
This commit is contained in:
@@ -106,6 +106,16 @@
|
||||
|
||||
<hr class="m-rule--double" />
|
||||
|
||||
@* ---------- By detector kind ---------- *@
|
||||
<section class="m-insights__section m-rise m-rise-3" data-test="insights-by-kind">
|
||||
<header class="m-insights__section-head">
|
||||
<span class="m-kicker">@L["Insights.Section.ByKind"]</span>
|
||||
</header>
|
||||
@RenderBucketTable(vm.ByKind, BucketRenderKind.Kind)
|
||||
</section>
|
||||
|
||||
<hr class="m-rule--double" />
|
||||
|
||||
@* ---------- By sport ---------- *@
|
||||
<section class="m-insights__section m-rise m-rise-3" data-test="insights-by-sport">
|
||||
<header class="m-insights__section-head">
|
||||
@@ -630,6 +640,7 @@
|
||||
Severity,
|
||||
Sport,
|
||||
Score,
|
||||
Kind,
|
||||
}
|
||||
|
||||
private AnomalyInsightsVm? _vm;
|
||||
@@ -826,6 +837,23 @@
|
||||
}
|
||||
break;
|
||||
}
|
||||
case BucketRenderKind.Kind:
|
||||
{
|
||||
var trimmed = key.StartsWith(OutcomeBucketKeys.KindPrefix, StringComparison.Ordinal)
|
||||
? key.Substring(OutcomeBucketKeys.KindPrefix.Length)
|
||||
: key;
|
||||
var locKey = trimmed switch
|
||||
{
|
||||
nameof(AnomalyKind.SuspensionFlip) => "Anomaly.Kind.SuspensionFlip",
|
||||
nameof(AnomalyKind.SteamMove) => "Anomaly.Kind.SteamMove",
|
||||
nameof(AnomalyKind.SuspensionFreeze) => "Anomaly.Kind.SuspensionFreeze",
|
||||
nameof(AnomalyKind.OverroundCompression) => "Anomaly.Kind.OverroundCompression",
|
||||
_ => null,
|
||||
};
|
||||
if (locKey is null) builder.AddContent(0, trimmed);
|
||||
else builder.AddContent(0, L[locKey]);
|
||||
break;
|
||||
}
|
||||
case BucketRenderKind.Score:
|
||||
default:
|
||||
{
|
||||
|
||||
Reference in New Issue
Block a user