Fix three matrix generation bugs found in a real generated sheet
Diagnosed directly against a real generated spreadsheet the user
shared, rather than guessing:
- Player/opponent names came out blank ("- Necrons" instead of "John
Smith - Necrons"): fullName() takes the nested BCP user object
({firstName, lastName}), not the roster/teamplayer entry itself —
same convention main.js's own playerRowHTML() already follows, now
matched here via a shared nameOf() helper (with the same nickname
fallback playerRowHTML uses too).
- That cascaded into a worse, harder-to-spot bug: with the name blank,
a win-rate cell became literally " (62.5%)". Writes used
valueInputOption: USER_ENTERED, which smart-parses values — Sheets
read "(62.5%)" as accounting notation for a negative number and
silently replaced the cell with e.g. "-62.50%" instead of the
intended text. Same setting explains the "#ERROR!" cells: some raw
list text got parsed as a formula. Switched to RAW, which stores
exactly what's sent — correct for the plain descriptive labels this
writes, never intended to be interpreted.
- Lists with a New Recruit "+++...+++" header came through raw,
uncleaned: parseNRGW/parseNRTournament were vendored (needed for
NR_GW/NR_TOURMENT detectFormat() results) but never actually wired
into the format dispatch table, and aren't re-exported from the
package's own index.js (only from the internal modules/parsers.js,
which is now imported directly). Verified against the tool's own
NR-GW/NR-Tournament sample fixtures with real Node before committing
— both now correctly detected and cleaned.
Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01SBzcNkW7JcYipnAX6HfgmK
This commit is contained in:
@@ -13,7 +13,10 @@
|
||||
Drive's API returns 404, not 403, for a file outside the token's grant,
|
||||
so it looks identical to a bad file ID) and spreadsheets. */
|
||||
|
||||
import { detectFormat, parseGwAppV11, parseWarOrganV11, parseV11List, generateDiscordText, buildAbbreviationIndex } from "./vendor/40k-compactor/index.js";
|
||||
import { generateDiscordText, buildAbbreviationIndex } from "./vendor/40k-compactor/index.js";
|
||||
// parseNRGW/parseNRTournament aren't re-exported from the package's own
|
||||
// index.js (only from this internal module) — pulled from here directly.
|
||||
import { detectFormat, parseGwAppV11, parseWarOrganV11, parseV11List, parseNRGW, parseNRTournament } from "./vendor/40k-compactor/modules/parsers.js";
|
||||
import skippableWargear from "./vendor/40k-compactor/skippable_wargear.json";
|
||||
|
||||
const GOOGLE_CLIENT_ID = "698705216189-rqbg3f7vlosi3077c3n410a7lpmsn16r.apps.googleusercontent.com";
|
||||
@@ -25,14 +28,12 @@ const SHEETS_API = "https://sheets.googleapis.com/v4/spreadsheets";
|
||||
|
||||
/* ---- List cleaning (vendored 40k-compactor — see vendor/40k-compactor/NOTICE.md) ---- */
|
||||
|
||||
// detectFormat() only distinguishes the 11th-edition dialects this parser
|
||||
// set understands; NR_TOURNAMENT isn't separately parseable from the
|
||||
// functions this tool exports (no dedicated parser in the vendored code),
|
||||
// so it falls back to the generic V11 parser same as an unrecognized format.
|
||||
const PARSERS = {
|
||||
GW_APP_V11: parseGwAppV11,
|
||||
WAR_ORGAN_V11: parseWarOrganV11,
|
||||
V11_GENERIC: parseV11List,
|
||||
NR_GW: parseNRGW,
|
||||
NR_TOURNAMENT: parseNRTournament,
|
||||
};
|
||||
|
||||
// Returns cleaned, sheet-friendly (plain, not Discord-markdown) list text,
|
||||
@@ -125,10 +126,17 @@ const batchUpdate = (token, fileId, requests) =>
|
||||
body: JSON.stringify({ requests }),
|
||||
});
|
||||
|
||||
// RAW, not USER_ENTERED: everything written here (names, factions, win
|
||||
// rates, list text) is a literal label, never meant to be interpreted.
|
||||
// USER_ENTERED's smart-parsing was actively harmful — e.g. a blank name
|
||||
// next to a win rate produced " (62.5%)", which Sheets read as accounting
|
||||
// notation for a *negative* number and silently replaced the whole cell
|
||||
// with e.g. "-62.50%"; list text starting with certain characters came
|
||||
// back as "#ERROR!" for the same reason.
|
||||
const batchUpdateValues = (token, fileId, data) =>
|
||||
googleFetch(`${SHEETS_API}/${fileId}/values:batchUpdate`, token, {
|
||||
method: "POST",
|
||||
body: JSON.stringify({ valueInputOption: "USER_ENTERED", data }),
|
||||
body: JSON.stringify({ valueInputOption: "RAW", data }),
|
||||
});
|
||||
|
||||
/* ---- Template layout ----
|
||||
@@ -243,10 +251,14 @@ const a1 = (sheetTitle, col, row) => `'${sheetTitle.replace(/'/g, "''")}'!${colL
|
||||
|
||||
/* ---- Row/column value builders ---- */
|
||||
|
||||
// fullName() takes the nested BCP user object ({firstName, lastName}), not
|
||||
// the roster/teamplayer entry itself — same convention main.js's own
|
||||
// playerRowHTML() uses, matched here.
|
||||
const nameOf = member => fullNameOf(member.user || {}) || member.user?.nickname || "Unknown";
|
||||
|
||||
function playerLabel(member) {
|
||||
const name = fullNameOf(member);
|
||||
const faction = member.faction?.name || "Unknown";
|
||||
return `${name} - ${faction}`;
|
||||
return `${nameOf(member)} - ${faction}`;
|
||||
}
|
||||
|
||||
// fullName/fmtWpct live in main.js today; passed in rather than duplicated.
|
||||
@@ -258,11 +270,10 @@ export function configureMatrixHelpers({ fullName, fmtWpct }) {
|
||||
}
|
||||
|
||||
function opponentNameAndWinRate(member, careerRecords) {
|
||||
const name = fullNameOf(member);
|
||||
const uid = member.userId || member.user?.id;
|
||||
const rec = careerRecords.get(uid);
|
||||
const wpct = rec ? fmtWpctOf(rec.w, rec.l, rec.t) : "—";
|
||||
return `${name} (${wpct})`;
|
||||
return `${nameOf(member)} (${wpct})`;
|
||||
}
|
||||
|
||||
/* ---- Orchestration ---- */
|
||||
|
||||
Reference in New Issue
Block a user