"""Two-sided contract checker for reading a real .xlsx workbook's effective cell grid.""" import json REQUIREMENTS = [ {"id": "R1", "text": "the output grid has exactly the given number of rows and columns"}, {"id": "R2", "text": "every cell covered by a merged range holds that range's value, not " "just the top-left cell of the range"}, {"id": "R3", "text": "an unmerged cell that was given a value holds that value exactly"}, {"id": "R4", "text": "a cell that was never set and is not part of any merge is null"}, ] INPUT_SUFFIX = ".xlsx" def _expected_grid(spec: dict) -> list: grid = [[None] * spec["cols"] for _ in range(spec["rows"])] for m in spec.get("merges", []): (r0, c0), (r1, c1) = m["cells"] for r in range(r0, r1 + 1): for c in range(c0, c1 + 1): grid[r][c] = m["value"] for key, value in spec.get("singles", {}).items(): r, c = (int(x) for x in key.split(",")) grid[r][c] = value return grid def build_input(spec: dict, out_path: str) -> None: from openpyxl import Workbook wb = Workbook() ws = wb.active for m in spec.get("merges", []): (r0, c0), (r1, c1) = m["cells"] ws.merge_cells(start_row=r0 + 1, start_column=c0 + 1, end_row=r1 + 1, end_column=c1 + 1) ws.cell(row=r0 + 1, column=c0 + 1).value = m["value"] for key, value in spec.get("singles", {}).items(): r, c = (int(x) for x in key.split(",")) ws.cell(row=r + 1, column=c + 1).value = value wb.save(out_path) def build_reference(spec: dict, out_path: str) -> None: with open(out_path, "w", encoding="utf-8") as fh: json.dump(_expected_grid(spec), fh) def check(spec: dict, path: str) -> list[tuple[str, bool, str]]: try: got = json.load(open(path, encoding="utf-8")) except Exception as e: return [(r["id"], False, f"could not read/parse output: {type(e).__name__}: {e}") for r in REQUIREMENTS] dims_ok = (isinstance(got, list) and len(got) == spec["rows"] and all(isinstance(row, list) and len(row) == spec["cols"] for row in got)) if not dims_ok: shape = [len(row) if isinstance(row, list) else "?" for row in got] if isinstance(got, list) else "not a list" return [ ("R1", False, f"expected {spec['rows']}x{spec['cols']}, got shape {shape}"), ("R2", False, "cannot check: wrong shape"), ("R3", False, "cannot check: wrong shape"), ("R4", False, "cannot check: wrong shape"), ] expected = _expected_grid(spec) merge_cells = {(r, c) for m in spec.get("merges", []) for r in range(m["cells"][0][0], m["cells"][1][0] + 1) for c in range(m["cells"][0][1], m["cells"][1][1] + 1)} single_cells = {tuple(int(x) for x in k.split(",")) for k in spec.get("singles", {})} empty_cells = {(r, c) for r in range(spec["rows"]) for c in range(spec["cols"]) if (r, c) not in merge_cells and (r, c) not in single_cells} merge_ok = all(got[r][c] == expected[r][c] for r, c in merge_cells) if merge_cells else True single_ok = all(got[r][c] == expected[r][c] for r, c in single_cells) if single_cells else True empty_ok = all(got[r][c] is None for r, c in empty_cells) if empty_cells else True bad_merge = [(r, c) for r, c in merge_cells if got[r][c] != expected[r][c]] bad_single = [(r, c) for r, c in single_cells if got[r][c] != expected[r][c]] bad_empty = [(r, c) for r, c in empty_cells if got[r][c] is not None] return [ ("R1", True, f"{spec['rows']}x{spec['cols']}"), ("R2", merge_ok, "every merged cell correct" if merge_ok else f"wrong at {bad_merge[:4]}: got {[got[r][c] for r, c in bad_merge[:4]]}"), ("R3", single_ok, "every unmerged value correct" if single_ok else f"wrong at {bad_single[:4]}"), ("R4", empty_ok, "untouched cells are null" if empty_ok else f"non-null at empty cells {bad_empty[:4]}: {[got[r][c] for r, c in bad_empty[:4]]}"), ] def _variant_row_major_strings(spec: dict, out_path: str) -> None: grid = [[(str(v) if v is not None else None) for v in row] for row in _expected_grid(spec)] with open(out_path, "w", encoding="utf-8") as fh: json.dump(grid, fh) def _variant_compact_json(spec: dict, out_path: str) -> None: with open(out_path, "w", encoding="utf-8") as fh: json.dump(_expected_grid(spec), fh, separators=(",", ":")) VARIANTS = [("stringified_values", _variant_row_major_strings), ("compact_json", _variant_compact_json)] def _sp_drop_merge_dup(path: str) -> str: p = path + ".s1" grid = json.load(open(path, encoding="utf-8")) seen = set() out = [] for r, row in enumerate(grid): new_row = [] for c, v in enumerate(row): if v is not None and v in seen: new_row.append(None) else: if v is not None: seen.add(v) new_row.append(v) out.append(new_row) json.dump(out, open(p, "w", encoding="utf-8")) return p def _sp_wrong_dims(path: str) -> str: p = path + ".s2" grid = json.load(open(path, encoding="utf-8")) out = grid[:-1] if len(grid) > 1 else grid json.dump(out, open(p, "w", encoding="utf-8")) return p def _sp_wrong_value(path: str) -> str: p = path + ".s3" grid = json.load(open(path, encoding="utf-8")) counts: dict = {} for row in grid: for v in row: if v is not None: counts[v] = counts.get(v, 0) + 1 for row in grid: for i, v in enumerate(row): if isinstance(v, str) and counts.get(v) == 1: row[i] = v + "_WRONG" json.dump(grid, open(p, "w", encoding="utf-8")) return p json.dump(grid, open(p, "w", encoding="utf-8")) return p def _sp_fill_empty(path: str) -> str: p = path + ".s4" grid = json.load(open(path, encoding="utf-8")) for row in grid: for i, v in enumerate(row): if v is None: row[i] = "SHOULD_BE_NULL" json.dump(grid, open(p, "w", encoding="utf-8")) return p SPOILERS = [ ("R2", "drop_merge_duplication", _sp_drop_merge_dup), ("R1", "wrong_dims", _sp_wrong_dims), ("R3", "wrong_value", _sp_wrong_value), ("R4", "filled_empty_cells", _sp_fill_empty), ]