refactor: model independent spread metric rules
This commit is contained in:
@@ -1,4 +1,9 @@
|
|||||||
import type { SpreadInfoMetrics, SpreadMetricThresholds } from "./types";
|
import type {
|
||||||
|
SpreadInfoConfig,
|
||||||
|
SpreadInfoMetrics,
|
||||||
|
SpreadMetricFilterRule,
|
||||||
|
SpreadMetricThresholds
|
||||||
|
} from "./types";
|
||||||
|
|
||||||
interface FetchResponseLike {
|
interface FetchResponseLike {
|
||||||
json(): Promise<unknown>;
|
json(): Promise<unknown>;
|
||||||
@@ -10,13 +15,6 @@ type FetchLike = (
|
|||||||
init?: RequestInit
|
init?: RequestInit
|
||||||
) => Promise<FetchResponseLike>;
|
) => Promise<FetchResponseLike>;
|
||||||
|
|
||||||
export interface SpreadInfoConfig {
|
|
||||||
flowType: 0 | 1;
|
|
||||||
onlyAssign: boolean;
|
|
||||||
range: 2 | 3;
|
|
||||||
type: 1 | 2;
|
|
||||||
}
|
|
||||||
|
|
||||||
interface SpreadInfoClientOptions {
|
interface SpreadInfoClientOptions {
|
||||||
baseUrl?: string;
|
baseUrl?: string;
|
||||||
configs?: SpreadInfoConfig[];
|
configs?: SpreadInfoConfig[];
|
||||||
@@ -222,6 +220,32 @@ export function matchesSpreadThresholds(
|
|||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
|
export function normalizeSpreadInfoConfig(
|
||||||
|
config: SpreadInfoConfig
|
||||||
|
): SpreadInfoConfig {
|
||||||
|
return config.type === 1
|
||||||
|
? { ...config, flowType: 0, onlyAssign: false }
|
||||||
|
: { ...config };
|
||||||
|
}
|
||||||
|
|
||||||
|
export function buildSpreadInfoConfigKey(config: SpreadInfoConfig): string {
|
||||||
|
const normalized = normalizeSpreadInfoConfig(config);
|
||||||
|
return [
|
||||||
|
normalized.type,
|
||||||
|
normalized.onlyAssign ? 1 : 0,
|
||||||
|
normalized.flowType,
|
||||||
|
normalized.range
|
||||||
|
].join(":");
|
||||||
|
}
|
||||||
|
|
||||||
|
export function matchesSpreadMetricRule(
|
||||||
|
metrics: MappedSpreadInfoResponse,
|
||||||
|
rule: SpreadMetricFilterRule
|
||||||
|
): boolean {
|
||||||
|
const numericValue = readDisplayNumber(metrics[rule.metric]);
|
||||||
|
return numericValue !== null && numericValue >= rule.threshold;
|
||||||
|
}
|
||||||
|
|
||||||
function buildSpreadInfoColumnHeader(
|
function buildSpreadInfoColumnHeader(
|
||||||
config: SpreadInfoConfig,
|
config: SpreadInfoConfig,
|
||||||
metric: SpreadInfoMetricDefinition
|
metric: SpreadInfoMetricDefinition
|
||||||
|
|||||||
@@ -14,6 +14,21 @@ export interface BackendMetrics {
|
|||||||
|
|
||||||
export type SpreadInfoMetrics = Record<string, string>;
|
export type SpreadInfoMetrics = Record<string, string>;
|
||||||
|
|
||||||
|
export interface SpreadInfoConfig {
|
||||||
|
flowType: 0 | 1;
|
||||||
|
onlyAssign: boolean;
|
||||||
|
range: 2 | 3;
|
||||||
|
type: 1 | 2;
|
||||||
|
}
|
||||||
|
|
||||||
|
export type SpreadFilterMetric = "finishRate" | "interactionRate";
|
||||||
|
|
||||||
|
export interface SpreadMetricFilterRule {
|
||||||
|
config: SpreadInfoConfig;
|
||||||
|
metric: SpreadFilterMetric;
|
||||||
|
threshold: number;
|
||||||
|
}
|
||||||
|
|
||||||
export interface SpreadMetricThresholds {
|
export interface SpreadMetricThresholds {
|
||||||
averageCommentCount?: number;
|
averageCommentCount?: number;
|
||||||
averageDuration?: number;
|
averageDuration?: number;
|
||||||
@@ -25,12 +40,7 @@ export interface SpreadMetricThresholds {
|
|||||||
}
|
}
|
||||||
|
|
||||||
export interface SpreadThresholdFilter {
|
export interface SpreadThresholdFilter {
|
||||||
config: {
|
config: SpreadInfoConfig;
|
||||||
flowType: 0 | 1;
|
|
||||||
onlyAssign: boolean;
|
|
||||||
range: 2 | 3;
|
|
||||||
type: 1 | 2;
|
|
||||||
};
|
|
||||||
thresholds: SpreadMetricThresholds;
|
thresholds: SpreadMetricThresholds;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
+73
-27
@@ -1,11 +1,13 @@
|
|||||||
import { describe, expect, test, vi } from "vitest";
|
import { describe, expect, test, vi } from "vitest";
|
||||||
|
|
||||||
import {
|
import {
|
||||||
|
buildSpreadInfoConfigKey,
|
||||||
buildSpreadInfoColumns,
|
buildSpreadInfoColumns,
|
||||||
buildSpreadInfoUrl,
|
buildSpreadInfoUrl,
|
||||||
createSpreadInfoClient,
|
createSpreadInfoClient,
|
||||||
DEFAULT_SPREAD_INFO_CONFIGS,
|
DEFAULT_SPREAD_INFO_CONFIGS,
|
||||||
matchesSpreadThresholds,
|
matchesSpreadMetricRule,
|
||||||
|
normalizeSpreadInfoConfig,
|
||||||
mapSpreadInfoResponse
|
mapSpreadInfoResponse
|
||||||
} from "../src/content/market/spread-info";
|
} from "../src/content/market/spread-info";
|
||||||
|
|
||||||
@@ -167,46 +169,90 @@ describe("spread-info", () => {
|
|||||||
);
|
);
|
||||||
});
|
});
|
||||||
|
|
||||||
test("matches thresholds using display values and requires every filled threshold", () => {
|
test("normalizes fixed personal-video parameters before grouping", () => {
|
||||||
expect(
|
expect(
|
||||||
matchesSpreadThresholds(
|
normalizeSpreadInfoConfig({
|
||||||
|
flowType: 1,
|
||||||
|
onlyAssign: true,
|
||||||
|
range: 3,
|
||||||
|
type: 1
|
||||||
|
})
|
||||||
|
).toEqual({
|
||||||
|
flowType: 0,
|
||||||
|
onlyAssign: false,
|
||||||
|
range: 3,
|
||||||
|
type: 1
|
||||||
|
});
|
||||||
|
});
|
||||||
|
|
||||||
|
test("uses all normalized video dimensions in the config key", () => {
|
||||||
|
expect(
|
||||||
|
buildSpreadInfoConfigKey({
|
||||||
|
flowType: 1,
|
||||||
|
onlyAssign: true,
|
||||||
|
range: 3,
|
||||||
|
type: 1
|
||||||
|
})
|
||||||
|
).toBe(
|
||||||
|
buildSpreadInfoConfigKey({
|
||||||
|
flowType: 0,
|
||||||
|
onlyAssign: false,
|
||||||
|
range: 3,
|
||||||
|
type: 1
|
||||||
|
})
|
||||||
|
);
|
||||||
|
|
||||||
|
expect(
|
||||||
|
buildSpreadInfoConfigKey({
|
||||||
|
flowType: 0,
|
||||||
|
onlyAssign: false,
|
||||||
|
range: 2,
|
||||||
|
type: 2
|
||||||
|
})
|
||||||
|
).not.toBe(
|
||||||
|
buildSpreadInfoConfigKey({
|
||||||
|
flowType: 0,
|
||||||
|
onlyAssign: false,
|
||||||
|
range: 3,
|
||||||
|
type: 2
|
||||||
|
})
|
||||||
|
);
|
||||||
|
});
|
||||||
|
|
||||||
|
test("matches only the metric named by one filter rule", () => {
|
||||||
|
expect(
|
||||||
|
matchesSpreadMetricRule(
|
||||||
{
|
{
|
||||||
averageDuration: "56",
|
|
||||||
finishRate: "28.24%",
|
finishRate: "28.24%",
|
||||||
interactionRate: "4.02%",
|
interactionRate: "4.02%"
|
||||||
playMedian: "10913233"
|
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
averageDuration: 50,
|
config: {
|
||||||
finishRate: 28,
|
flowType: 0,
|
||||||
interactionRate: 4,
|
onlyAssign: false,
|
||||||
playMedian: 10000000
|
range: 2,
|
||||||
|
type: 1
|
||||||
|
},
|
||||||
|
metric: "finishRate",
|
||||||
|
threshold: 28
|
||||||
}
|
}
|
||||||
)
|
)
|
||||||
).toBe(true);
|
).toBe(true);
|
||||||
|
|
||||||
expect(
|
expect(
|
||||||
matchesSpreadThresholds(
|
matchesSpreadMetricRule(
|
||||||
{
|
|
||||||
averageDuration: "56",
|
|
||||||
finishRate: "28.24%",
|
|
||||||
interactionRate: "4.02%",
|
|
||||||
playMedian: "10913233"
|
|
||||||
},
|
|
||||||
{
|
|
||||||
averageDuration: 57
|
|
||||||
}
|
|
||||||
)
|
|
||||||
).toBe(false);
|
|
||||||
|
|
||||||
expect(
|
|
||||||
matchesSpreadThresholds(
|
|
||||||
{
|
{
|
||||||
finishRate: "28.24%"
|
finishRate: "28.24%"
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
finishRate: 20,
|
config: {
|
||||||
interactionRate: 1
|
flowType: 0,
|
||||||
|
onlyAssign: false,
|
||||||
|
range: 2,
|
||||||
|
type: 1
|
||||||
|
},
|
||||||
|
metric: "interactionRate",
|
||||||
|
threshold: 1
|
||||||
}
|
}
|
||||||
)
|
)
|
||||||
).toBe(false);
|
).toBe(false);
|
||||||
|
|||||||
Reference in New Issue
Block a user