Prepare scanner branch for merge
This commit is contained in:
@@ -0,0 +1,109 @@
|
||||
const fs = require("node:fs");
|
||||
const path = require("node:path");
|
||||
|
||||
const EVAL_FIELDS = new Set(["name", "slot", "level", "mainStat", "mainValue", "setName", "equipped", "substats"]);
|
||||
|
||||
function argValue(name, fallback = "") {
|
||||
const prefix = `--${name}=`;
|
||||
const match = process.argv.find((entry) => entry.startsWith(prefix));
|
||||
return match ? match.slice(prefix.length) : fallback;
|
||||
}
|
||||
|
||||
function parseExpectedFields() {
|
||||
const inlineJson = argValue("expect-json");
|
||||
const expectFile = argValue("expect-file");
|
||||
if (!inlineJson && !expectFile) {
|
||||
throw new Error("Missing expected labels. Pass --expect-json=... or --expect-file=...");
|
||||
}
|
||||
const raw = (inlineJson || fs.readFileSync(path.resolve(expectFile), "utf8")).replace(/^\uFEFF/, "");
|
||||
const parsed = JSON.parse(raw);
|
||||
if (!parsed || typeof parsed !== "object" || Array.isArray(parsed)) {
|
||||
throw new Error("Expected labels must be a JSON object.");
|
||||
}
|
||||
const expect = {};
|
||||
for (const [field, value] of Object.entries(parsed)) {
|
||||
if (!EVAL_FIELDS.has(field)) throw new Error(`Unknown expected field: ${field}`);
|
||||
if (field === "level") {
|
||||
if (!Number.isInteger(value) || value < 0) throw new Error("Expected level must be a non-negative integer.");
|
||||
expect[field] = value;
|
||||
continue;
|
||||
}
|
||||
if (field === "substats") {
|
||||
if (!Array.isArray(value) || !value.every((entry) => typeof entry === "string" && entry.trim())) {
|
||||
throw new Error("Expected substats must be a non-empty string array.");
|
||||
}
|
||||
expect[field] = value;
|
||||
continue;
|
||||
}
|
||||
if (typeof value !== "string" || !value.trim()) throw new Error(`Expected ${field} must be a non-empty string.`);
|
||||
expect[field] = value;
|
||||
}
|
||||
if (Object.keys(expect).length === 0) throw new Error("Expected labels must include at least one field.");
|
||||
return expect;
|
||||
}
|
||||
|
||||
function loadCandidate(inputPath, candidateId) {
|
||||
const payload = JSON.parse(fs.readFileSync(path.resolve(inputPath), "utf8"));
|
||||
const candidates = Array.isArray(payload?.candidates) ? payload.candidates : [];
|
||||
const candidate = candidates.find((entry) => entry.id === candidateId);
|
||||
if (!candidate) throw new Error(`Candidate not found: ${candidateId}`);
|
||||
if (!candidate.ocr || typeof candidate.ocr !== "object" || Object.keys(candidate.ocr).length === 0) {
|
||||
throw new Error(`Candidate has no OCR payload: ${candidateId}`);
|
||||
}
|
||||
return candidate;
|
||||
}
|
||||
|
||||
function stableId(value) {
|
||||
return String(value || "")
|
||||
.replace(/[^0-9A-Za-z]+/g, "-")
|
||||
.replace(/^-+|-+$/g, "")
|
||||
.slice(0, 80) || "review-case";
|
||||
}
|
||||
|
||||
function objectLiteral(value, indent = 2) {
|
||||
return JSON.stringify(value, null, indent).replace(/"([A-Za-z_$][0-9A-Za-z_$]*)":/g, "$1:");
|
||||
}
|
||||
|
||||
function snippetFor(candidate, expect) {
|
||||
const id = stableId(argValue("id", `confirmed-${candidate.id}`));
|
||||
const entry = {
|
||||
id,
|
||||
confirmed: true,
|
||||
ocr: candidate.ocr,
|
||||
expect,
|
||||
meta: {
|
||||
source: "review-sample",
|
||||
resolution: candidate.resolution || undefined,
|
||||
note: `${candidate.reason || "review-sample"} | savedAt=${candidate.savedAt || "unknown"} | sourceCandidate=${candidate.id}`,
|
||||
},
|
||||
};
|
||||
return `${objectLiteral(entry, 2)},\n`;
|
||||
}
|
||||
|
||||
function defaultInputPath() {
|
||||
return path.join(process.cwd(), "outputs", "review-eval-candidates", "review-eval-candidates.json");
|
||||
}
|
||||
|
||||
function main() {
|
||||
const inputPath = argValue("input", defaultInputPath());
|
||||
const candidateId = argValue("candidate");
|
||||
if (!candidateId) throw new Error("Missing candidate id. Pass --candidate=<id>.");
|
||||
const outputPath = path.resolve(argValue("out", path.join(process.cwd(), "outputs", "review-eval-candidates", `${stableId(candidateId)}.confirmed.ts`)));
|
||||
const candidate = loadCandidate(inputPath, candidateId);
|
||||
const expect = parseExpectedFields();
|
||||
fs.mkdirSync(path.dirname(outputPath), { recursive: true });
|
||||
const snippet = snippetFor(candidate, expect);
|
||||
fs.writeFileSync(outputPath, snippet, "utf8");
|
||||
console.log(JSON.stringify({ ok: true, candidateId, outputPath, labeledFields: Object.keys(expect) }, null, 2));
|
||||
}
|
||||
|
||||
if (require.main === module) {
|
||||
main();
|
||||
}
|
||||
|
||||
module.exports = {
|
||||
loadCandidate,
|
||||
parseExpectedFields,
|
||||
snippetFor,
|
||||
stableId,
|
||||
};
|
||||
Reference in New Issue
Block a user