From a9f02bfae5b6579f935a711f70bdcb1a59bfcfb3 Mon Sep 17 00:00:00 2001 From: Darshan Mahale Date: Sat, 24 Oct 2026 16:41:39 +0530 Subject: [PATCH] Fix boolean XLSX header parity --- assets/profiler-engine.js | 19 ++++++++++++++++++- tests/test_xlsx_edge_cases.py | 19 +++++++++++++++++++ 2 files changed, 37 insertions(+), 1 deletion(-) diff --git a/assets/profiler-engine.js b/assets/profiler-engine.js index 5b9d68d6..3973c7a2 100644 --- a/assets/profiler-engine.js +++ b/assets/profiler-engine.js @@ -632,6 +632,22 @@ }); } + function xlsxHeaderText(value) { + if (typeof value === 'boolean') return value ? 'True' : 'False'; + return value === null || value === undefined ? '' : String(value); + } + + function normalizeXlsxBooleanHeaderCells(sheet) { + if (!sheet['!ref']) return; + var range = global.XLSX.utils.decode_range(sheet['!ref']); + for (var c = range.s.c; c <= range.e.c; c++) { + var cell = sheet[global.XLSX.utils.encode_cell({ r: range.s.r, c: c })]; + if (cell && cell.t === 'b' && typeof cell.v === 'boolean') { + cell.w = xlsxHeaderText(cell.v); + } + } + } + async function parseXLSX(arrayBuffer) { await loadSheetJS(); @@ -649,6 +665,7 @@ var date1904 = !!(workbook.Workbook && workbook.Workbook.WBProps && workbook.Workbook.WBProps.date1904); stringifyXlsxDateCells(sheet, date1904); + normalizeXlsxBooleanHeaderCells(sheet); var records = global.XLSX.utils.sheet_to_json(sheet, { defval: null, raw: true }); if (records.length === 0) { @@ -658,7 +675,7 @@ // read the raw header row instead of erroring, to match. var headerRow = global.XLSX.utils.sheet_to_json(sheet, { header: 1, raw: true })[0] || []; var columns = headerRow.map(function (h) { - return h === null || h === undefined ? '' : String(h); + return xlsxHeaderText(h); }); return { table: { columns: columns, rows: [] }, ignoredSheets: workbook.SheetNames.slice(1), sheetName: sheetName }; } diff --git a/tests/test_xlsx_edge_cases.py b/tests/test_xlsx_edge_cases.py index 32d1bf5f..52583088 100644 --- a/tests/test_xlsx_edge_cases.py +++ b/tests/test_xlsx_edge_cases.py @@ -221,3 +221,22 @@ def test_xlsx_numeric_header_cells_are_text_in_both_engines(tmp_path): js = [d["name"] for d in json.loads(result.stdout)["dimensions"]] assert py == js == ["2020", "sex"] assert all(isinstance(name, str) for name in py) + + +@requires_openpyxl +def test_xlsx_boolean_header_cells_match_python_text_in_both_engines(tmp_path): + """#1143: boolean headers use Python's "True"/"False" spelling, without changing text headers.""" + from faircode import profile + + wb = openpyxl.Workbook() + ws = wb.active + ws.append([True, False, "TRUE", "FALSE", "sex"]) + for i in range(30): + ws.append([i % 3, (i + 1) % 2, i % 2, (i + 1) % 2, "m" if i % 2 else "f"]) + path = _save_workbook(tmp_path, wb, "boolean_header.xlsx") + py = [d["name"] for d in profile(read_table(str(path)))["dimensions"]] + result = _run_js_parse(path) + assert result.returncode == 0, result.stderr + js = [d["name"] for d in json.loads(result.stdout)["dimensions"]] + assert py == js == ["True", "False", "TRUE", "FALSE", "sex"] + assert all(isinstance(name, str) for name in py)