mirror of
https://github.com/samsonjs/spirit-tracker.git
synced 2026-04-27 15:07:43 +00:00
UX Improvements
This commit is contained in:
parent
bcdb5a5840
commit
37796cbcbc
1 changed files with 20 additions and 57 deletions
|
|
@ -2,17 +2,17 @@
|
||||||
"use strict";
|
"use strict";
|
||||||
|
|
||||||
/*
|
/*
|
||||||
Compare rank placement between AB and BC "common_listings_*_top*.json" reports.
|
Print local link URLs for SKUs with largest rank discrepancy between AB and BC lists.
|
||||||
|
|
||||||
Usage:
|
Usage:
|
||||||
node scripts/rank_discrepency.js \
|
node scripts/rank_discrepency_links.js \
|
||||||
--ab reports/common_listings_ab_top1000.json \
|
--ab reports/common_listings_ab_top1000.json \
|
||||||
--bc reports/common_listings_bc_top1000.json \
|
--bc reports/common_listings_bc_top1000.json \
|
||||||
--top 50
|
--top 50 \
|
||||||
|
--base "http://127.0.0.1:8080/#/link/?left="
|
||||||
|
|
||||||
Notes:
|
Output:
|
||||||
- Rank = index in payload.rows (1-based).
|
http://127.0.0.1:8080/#/link/?left=<urlencoded canonSku>
|
||||||
- Only compares SKUs that exist in BOTH files (unless --include-missing).
|
|
||||||
*/
|
*/
|
||||||
|
|
||||||
const fs = require("fs");
|
const fs = require("fs");
|
||||||
|
|
@ -27,8 +27,9 @@ function parseArgs(argv) {
|
||||||
ab: "reports/common_listings_ab_top1000.json",
|
ab: "reports/common_listings_ab_top1000.json",
|
||||||
bc: "reports/common_listings_bc_top1000.json",
|
bc: "reports/common_listings_bc_top1000.json",
|
||||||
top: 50,
|
top: 50,
|
||||||
includeMissing: false,
|
|
||||||
minDiscrep: 1,
|
minDiscrep: 1,
|
||||||
|
includeMissing: false,
|
||||||
|
base: "http://127.0.0.1:8080/#/link/?left=",
|
||||||
};
|
};
|
||||||
for (let i = 0; i < argv.length; i++) {
|
for (let i = 0; i < argv.length; i++) {
|
||||||
const a = argv[i];
|
const a = argv[i];
|
||||||
|
|
@ -37,6 +38,7 @@ function parseArgs(argv) {
|
||||||
else if (a === "--top" && argv[i + 1]) out.top = Number(argv[++i]) || out.top;
|
else if (a === "--top" && argv[i + 1]) out.top = Number(argv[++i]) || out.top;
|
||||||
else if (a === "--min" && argv[i + 1]) out.minDiscrep = Number(argv[++i]) || out.minDiscrep;
|
else if (a === "--min" && argv[i + 1]) out.minDiscrep = Number(argv[++i]) || out.minDiscrep;
|
||||||
else if (a === "--include-missing") out.includeMissing = true;
|
else if (a === "--include-missing") out.includeMissing = true;
|
||||||
|
else if (a === "--base" && argv[i + 1]) out.base = String(argv[++i] || out.base);
|
||||||
}
|
}
|
||||||
return out;
|
return out;
|
||||||
}
|
}
|
||||||
|
|
@ -46,16 +48,13 @@ function buildRankMap(payload) {
|
||||||
const map = new Map();
|
const map = new Map();
|
||||||
for (let i = 0; i < rows.length; i++) {
|
for (let i = 0; i < rows.length; i++) {
|
||||||
const r = rows[i];
|
const r = rows[i];
|
||||||
if (!r || !r.canonSku) continue;
|
const k = r?.canonSku;
|
||||||
map.set(String(r.canonSku), { rank: i + 1, row: r });
|
if (!k) continue;
|
||||||
|
map.set(String(k), { rank: i + 1, row: r });
|
||||||
}
|
}
|
||||||
return map;
|
return map;
|
||||||
}
|
}
|
||||||
|
|
||||||
function fmtMoney(n) {
|
|
||||||
return Number.isFinite(n) ? `$${n.toFixed(2)}` : "";
|
|
||||||
}
|
|
||||||
|
|
||||||
function main() {
|
function main() {
|
||||||
const args = parseArgs(process.argv.slice(2));
|
const args = parseArgs(process.argv.slice(2));
|
||||||
const repoRoot = process.cwd();
|
const repoRoot = process.cwd();
|
||||||
|
|
@ -86,64 +85,28 @@ function main() {
|
||||||
|
|
||||||
if (discrep !== Infinity && discrep < args.minDiscrep) continue;
|
if (discrep !== Infinity && discrep < args.minDiscrep) continue;
|
||||||
|
|
||||||
const rep = (a?.row?.representative || b?.row?.representative) ?? null;
|
|
||||||
const name = rep?.name || "";
|
|
||||||
const priceNum = rep?.priceNum;
|
|
||||||
const url = rep?.url || "";
|
|
||||||
|
|
||||||
diffs.push({
|
diffs.push({
|
||||||
canonSku,
|
canonSku,
|
||||||
discrep,
|
discrep,
|
||||||
rankAB,
|
// tie-breakers
|
||||||
rankBC,
|
sumRank: (rankAB ?? 1e9) + (rankBC ?? 1e9),
|
||||||
storeCountAB: a?.row?.storeCount ?? null,
|
|
||||||
storeCountBC: b?.row?.storeCount ?? null,
|
|
||||||
name,
|
|
||||||
price: fmtMoney(priceNum),
|
|
||||||
url,
|
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
diffs.sort((x, y) => {
|
diffs.sort((x, y) => {
|
||||||
if (y.discrep !== x.discrep) return y.discrep - x.discrep;
|
if (y.discrep !== x.discrep) return y.discrep - x.discrep;
|
||||||
// tie-breaker: best average rank (smaller is "higher")
|
if (x.sumRank !== y.sumRank) return x.sumRank - y.sumRank;
|
||||||
const ax = (x.rankAB ?? 1e9) + (x.rankBC ?? 1e9);
|
|
||||||
const ay = (y.rankAB ?? 1e9) + (y.rankBC ?? 1e9);
|
|
||||||
if (ax !== ay) return ax - ay;
|
|
||||||
return String(x.canonSku).localeCompare(String(y.canonSku));
|
return String(x.canonSku).localeCompare(String(y.canonSku));
|
||||||
});
|
});
|
||||||
|
|
||||||
const top = diffs.slice(0, args.top);
|
const top = diffs.slice(0, args.top);
|
||||||
|
|
||||||
console.log(
|
|
||||||
`AB: ${path.relative(repoRoot, abPath)} (rows=${ab?.rows?.length ?? 0})`
|
|
||||||
);
|
|
||||||
console.log(
|
|
||||||
`BC: ${path.relative(repoRoot, bcPath)} (rows=${bc?.rows?.length ?? 0})`
|
|
||||||
);
|
|
||||||
console.log(
|
|
||||||
`Showing top ${top.length} by |rankAB-rankBC| (min=${args.minDiscrep})\n`
|
|
||||||
);
|
|
||||||
|
|
||||||
for (const d of top) {
|
for (const d of top) {
|
||||||
const ra = d.rankAB === null ? "—" : String(d.rankAB);
|
// examples:
|
||||||
const rb = d.rankBC === null ? "—" : String(d.rankBC);
|
// 884096 -> left=884096
|
||||||
const sca = d.storeCountAB === null ? "—" : String(d.storeCountAB);
|
// id:1049355 -> left=id%3A1049355
|
||||||
const scb = d.storeCountBC === null ? "—" : String(d.storeCountBC);
|
// u:bb504a62 -> left=u%3Abb504a62
|
||||||
|
console.log(args.base + encodeURIComponent(d.canonSku));
|
||||||
console.log(
|
|
||||||
[
|
|
||||||
`Δ=${d.discrep}`,
|
|
||||||
`AB#${ra} (stores=${sca})`,
|
|
||||||
`BC#${rb} (stores=${scb})`,
|
|
||||||
`sku=${d.canonSku}`,
|
|
||||||
d.price ? `rep=${d.price}` : "",
|
|
||||||
d.name ? `name="${d.name.replace(/\s+/g, " ").trim().slice(0, 120)}"` : "",
|
|
||||||
]
|
|
||||||
.filter(Boolean)
|
|
||||||
.join(" | ")
|
|
||||||
);
|
|
||||||
if (d.url) console.log(` ${d.url}`);
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
||||||
Loading…
Reference in a new issue