fix: derive missing A3 batch metrics
This commit is contained in:
parent
fb45f0cea8
commit
b3bcc2af45
@ -76,11 +76,13 @@ export function mapBackendMetricsSearchResponse(payload: unknown): BackendMetric
|
|||||||
|
|
||||||
return [
|
return [
|
||||||
{
|
{
|
||||||
a3IncreaseCount: formatDecimalValue(row.avg_a3_increase_cnt),
|
a3IncreaseCount: formatDecimalValue(
|
||||||
|
readAverageA3IncreaseCount(row)
|
||||||
|
),
|
||||||
afterViewSearchCount: formatDecimalValue(row.avg_after_view_search_cnt),
|
afterViewSearchCount: formatDecimalValue(row.avg_after_view_search_cnt),
|
||||||
afterViewSearchRate: formatRateValue(row.avg_after_view_search_rate),
|
afterViewSearchRate: formatRateValue(row.avg_after_view_search_rate),
|
||||||
cpSearch: formatDecimalValue(row.cp_search),
|
cpSearch: formatDecimalValue(row.cp_search),
|
||||||
cpa3: formatDecimalValue(row.cpa3),
|
cpa3: formatDecimalValue(readCpa3Value(row)),
|
||||||
newA3Rate: formatRateValue(row.avg_new_a3_rate),
|
newA3Rate: formatRateValue(row.avg_new_a3_rate),
|
||||||
starId: row.star_id
|
starId: row.star_id
|
||||||
}
|
}
|
||||||
@ -88,6 +90,84 @@ export function mapBackendMetricsSearchResponse(payload: unknown): BackendMetric
|
|||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
|
function readAverageA3IncreaseCount(row: Record<string, unknown>): number | null {
|
||||||
|
const directAverage = readFiniteNumber(row.avg_a3_increase_cnt);
|
||||||
|
if (directAverage !== null) {
|
||||||
|
return directAverage;
|
||||||
|
}
|
||||||
|
|
||||||
|
const totalNewA3 = readTotalNewA3Value(row);
|
||||||
|
const videoCount =
|
||||||
|
readFiniteNumber(row.video_count) ?? readNestedVideoCount(row.videos);
|
||||||
|
if (totalNewA3 === null || videoCount === null || videoCount <= 0) {
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
|
||||||
|
return totalNewA3 / videoCount;
|
||||||
|
}
|
||||||
|
|
||||||
|
function readCpa3Value(row: Record<string, unknown>): number | null {
|
||||||
|
const directCpa3 = readFiniteNumber(row.cpa3);
|
||||||
|
if (directCpa3 !== null) {
|
||||||
|
return directCpa3;
|
||||||
|
}
|
||||||
|
|
||||||
|
const totalCost = readFiniteNumber(row.total_estimated_video_cost);
|
||||||
|
const totalNewA3 = readTotalNewA3Value(row);
|
||||||
|
if (totalCost === null || totalNewA3 === null || totalNewA3 <= 0) {
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
|
||||||
|
return totalCost / totalNewA3;
|
||||||
|
}
|
||||||
|
|
||||||
|
function readTotalNewA3Value(row: Record<string, unknown>): number | null {
|
||||||
|
const derivedFromTotals = deriveTotalNewA3FromTotals(row);
|
||||||
|
if (derivedFromTotals !== null) {
|
||||||
|
return derivedFromTotals;
|
||||||
|
}
|
||||||
|
|
||||||
|
return deriveTotalNewA3FromVideos(row.videos);
|
||||||
|
}
|
||||||
|
|
||||||
|
function deriveTotalNewA3FromTotals(row: Record<string, unknown>): number | null {
|
||||||
|
const totalPlayCount = readFiniteNumber(row.total_play_cnt);
|
||||||
|
const averageNewA3Rate = readFiniteNumber(row.avg_new_a3_rate);
|
||||||
|
if (totalPlayCount === null || averageNewA3Rate === null) {
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
|
||||||
|
return totalPlayCount * averageNewA3Rate;
|
||||||
|
}
|
||||||
|
|
||||||
|
function deriveTotalNewA3FromVideos(value: unknown): number | null {
|
||||||
|
if (!Array.isArray(value)) {
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
|
||||||
|
let total = 0;
|
||||||
|
let hasFiniteValue = false;
|
||||||
|
value.forEach((video) => {
|
||||||
|
if (!isRecord(video)) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
const newA3 = readFiniteNumber(video.new_a3);
|
||||||
|
if (newA3 === null) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
hasFiniteValue = true;
|
||||||
|
total += newA3;
|
||||||
|
});
|
||||||
|
|
||||||
|
return hasFiniteValue ? total : null;
|
||||||
|
}
|
||||||
|
|
||||||
|
function readNestedVideoCount(value: unknown): number | null {
|
||||||
|
return Array.isArray(value) ? value.length : null;
|
||||||
|
}
|
||||||
|
|
||||||
function readResponseRows(payload: unknown): unknown[] | null {
|
function readResponseRows(payload: unknown): unknown[] | null {
|
||||||
if (!isRecord(payload) || payload.success !== true) {
|
if (!isRecord(payload) || payload.success !== true) {
|
||||||
return null;
|
return null;
|
||||||
@ -130,3 +210,8 @@ async function defaultFetch(input: string, init?: RequestInit) {
|
|||||||
function isRecord(value: unknown): value is Record<string, unknown> {
|
function isRecord(value: unknown): value is Record<string, unknown> {
|
||||||
return typeof value === "object" && value !== null;
|
return typeof value === "object" && value !== null;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
function readFiniteNumber(value: unknown): number | null {
|
||||||
|
const number = typeof value === "number" ? value : Number(value);
|
||||||
|
return Number.isFinite(number) ? number : null;
|
||||||
|
}
|
||||||
|
|||||||
@ -63,6 +63,40 @@ describe("backend-metrics-client", () => {
|
|||||||
]);
|
]);
|
||||||
});
|
});
|
||||||
|
|
||||||
|
test("derives A3 count and CPA3 from the live aggregate response shape", () => {
|
||||||
|
expect(
|
||||||
|
mapBackendMetricsSearchResponse({
|
||||||
|
data: {
|
||||||
|
data: [
|
||||||
|
{
|
||||||
|
avg_after_view_search_cnt: 25982,
|
||||||
|
avg_after_view_search_rate: 0.0010872130261527625,
|
||||||
|
avg_new_a3_rate: 0.11075860229946684,
|
||||||
|
cp_search: 21.168501270110077,
|
||||||
|
cpe: 0.630604497471276,
|
||||||
|
cpm: 23.014670324994974,
|
||||||
|
star_id: "7021245050621263906",
|
||||||
|
total_estimated_video_cost: 1100000,
|
||||||
|
total_play_cnt: 47795601,
|
||||||
|
video_count: 2
|
||||||
|
}
|
||||||
|
]
|
||||||
|
},
|
||||||
|
success: true
|
||||||
|
})
|
||||||
|
).toEqual([
|
||||||
|
{
|
||||||
|
a3IncreaseCount: "2,646,886.98",
|
||||||
|
afterViewSearchCount: "25,982.00",
|
||||||
|
afterViewSearchRate: "0.11%",
|
||||||
|
cpSearch: "21.17",
|
||||||
|
cpa3: "0.21",
|
||||||
|
newA3Rate: "11.08%",
|
||||||
|
starId: "7021245050621263906"
|
||||||
|
}
|
||||||
|
]);
|
||||||
|
});
|
||||||
|
|
||||||
test("posts star ids with bearer auth when searching backend metrics", async () => {
|
test("posts star ids with bearer auth when searching backend metrics", async () => {
|
||||||
const fetchImpl = async (_input: string, init?: RequestInit) => ({
|
const fetchImpl = async (_input: string, init?: RequestInit) => ({
|
||||||
json: async () => ({
|
json: async () => ({
|
||||||
|
|||||||
Loading…
x
Reference in New Issue
Block a user