mirror of
https://github.com/zkldi/Tachi.git
synced 2026-09-22 23:18:05 +03:00
fix: folder-to-go metric is wrong (#1681)
This commit is contained in:
@@ -69,43 +69,62 @@ export async function GetSessionFolderRaises(
|
||||
}
|
||||
|
||||
for (const [metric, conf] of Object.entries(enumMetrics)) {
|
||||
if (!info.isNewScore) {
|
||||
const enumIndexes = score.scoreData.enumIndexes;
|
||||
const newIdx = enumIndexes?.[metric as keyof typeof enumIndexes];
|
||||
|
||||
if (newIdx === undefined) {
|
||||
continue;
|
||||
}
|
||||
|
||||
// Index of this metric before this score. New scores had no previous
|
||||
// PB, so they crossed every threshold from the bottom.
|
||||
let oldIdx: number;
|
||||
|
||||
if (info.isNewScore) {
|
||||
oldIdx = -1;
|
||||
} else {
|
||||
const delta = info.deltas[metric];
|
||||
|
||||
if (delta === undefined || delta <= 0) {
|
||||
continue;
|
||||
}
|
||||
|
||||
oldIdx = newIdx - delta;
|
||||
}
|
||||
|
||||
const enumIndexes = score.scoreData.enumIndexes;
|
||||
const idx = enumIndexes?.[metric as keyof typeof enumIndexes];
|
||||
const minIdx = conf.values.indexOf(conf.minimumRelevantValue);
|
||||
|
||||
if (idx === undefined) {
|
||||
continue;
|
||||
}
|
||||
// This view is cumulative: reaching e.g. EX HARD CLEAR also counts as
|
||||
// a HARD CLEAR (and CLEAR, ...). So this score raised every enum
|
||||
// value-or-better bucket it crossed, i.e. every threshold in
|
||||
// (oldIdx, newIdx]. Only surface thresholds above the
|
||||
// minimum-relevant value (so we don't show NO PLAY / FAILED rows).
|
||||
const startIdx = Math.max(oldIdx, minIdx) + 1;
|
||||
|
||||
if (idx <= conf.values.indexOf(conf.minimumRelevantValue)) {
|
||||
continue;
|
||||
}
|
||||
|
||||
const valueRaw = (score.scoreData as Record<string, unknown>)[metric];
|
||||
|
||||
if (typeof valueRaw !== "string") {
|
||||
if (startIdx > newIdx) {
|
||||
continue;
|
||||
}
|
||||
|
||||
const folderIds = await folderIdsForChartCached(score.chartID, chartFolderCache);
|
||||
|
||||
for (const folderId of folderIds) {
|
||||
const key = bucketKey(folderId, metric, valueRaw);
|
||||
let set = bucket.get(key);
|
||||
for (let vi = startIdx; vi <= newIdx; vi++) {
|
||||
const value = conf.values[vi];
|
||||
|
||||
if (!set) {
|
||||
set = new Set();
|
||||
bucket.set(key, set);
|
||||
if (value === undefined) {
|
||||
continue;
|
||||
}
|
||||
|
||||
set.add(score.chartID);
|
||||
for (const folderId of folderIds) {
|
||||
const key = bucketKey(folderId, metric, value);
|
||||
let set = bucket.get(key);
|
||||
|
||||
if (!set) {
|
||||
set = new Set();
|
||||
bucket.set(key, set);
|
||||
}
|
||||
|
||||
set.add(score.chartID);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -143,7 +162,10 @@ export async function GetSessionFolderRaises(
|
||||
continue;
|
||||
}
|
||||
|
||||
const previousCount = dist.enumDist[metric]?.[value] ?? 0;
|
||||
// Cumulative: how many charts were already at this value-or-better before
|
||||
// the session. Pairs with the cumulative raise buckets above so that
|
||||
// `previousCount + raisedCharts.length` is the true value-or-better total.
|
||||
const previousCount = dist.cumulativeEnumDist[metric]?.[value] ?? 0;
|
||||
|
||||
out.push({
|
||||
folder,
|
||||
|
||||
@@ -165,6 +165,53 @@ describe("GET /api/v1/sessions/:sessionID/folder-raises", () => {
|
||||
expect(typeof hit.type).toBe("string");
|
||||
expect(typeof hit.value).toBe("string");
|
||||
});
|
||||
|
||||
it("treats raises cumulatively (an EX HARD CLEAR is also a HARD CLEAR)", async () => {
|
||||
const { sessionId } = await seedSessionFixture();
|
||||
const folderId = `F_folder_sess_cumulative_${sessionId}`;
|
||||
|
||||
await DB.insertInto("folder")
|
||||
.values({
|
||||
id: folderId,
|
||||
legacy_id: folderId,
|
||||
game: "iidx-sp",
|
||||
inactive: false,
|
||||
title: "Session Folder Cumulative Test",
|
||||
slug: folderId,
|
||||
where: `chart.id = '${CHART_PG}'`,
|
||||
version_filter: null,
|
||||
search_terms: [],
|
||||
})
|
||||
.execute();
|
||||
|
||||
await DB.insertInto("folder_chart_lookup")
|
||||
.values({ folder_id: folderId, chart_id: CHART_PG })
|
||||
.execute();
|
||||
|
||||
const res = await mockApi.get(`/api/v1/sessions/${sessionId}/folder-raises`);
|
||||
|
||||
expect(res.status).toBe(200);
|
||||
expect(res.body.success).toBe(true);
|
||||
|
||||
const lampRows = (
|
||||
res.body.body as Array<{
|
||||
folder: { folderID: string };
|
||||
raisedCharts: Array<string>;
|
||||
type: string;
|
||||
value: string;
|
||||
}>
|
||||
).filter((r) => r.folder.folderID === folderId && r.type === "lamp");
|
||||
|
||||
// The seeded score is an EX HARD CLEAR, so it should raise the EX HARD
|
||||
// CLEAR bucket *and* the HARD CLEAR (and CLEAR) value-or-better buckets.
|
||||
const exhc = lampRows.find((r) => r.value === "EX HARD CLEAR");
|
||||
const hc = lampRows.find((r) => r.value === "HARD CLEAR");
|
||||
|
||||
expect(exhc).toBeDefined();
|
||||
expect(exhc?.raisedCharts).toContain(CHART_PG);
|
||||
expect(hc).toBeDefined();
|
||||
expect(hc?.raisedCharts).toContain(CHART_PG);
|
||||
});
|
||||
});
|
||||
|
||||
describe("GET /api/v1/sessions/:sessionID/adjacent", () => {
|
||||
|
||||
Reference in New Issue
Block a user