From e61ff1d683fe8007d29ac8736c87c20914effadb Mon Sep 17 00:00:00 2001 From: Vladislav Kopnin Date: Wed, 9 Sep 2026 18:00:13 +0300 Subject: [PATCH] =?UTF-8?q?xlsx:=20=D1=87=D0=B8=D1=81=D1=82=D0=B8=D1=82?= =?UTF-8?q?=D1=8C=20=C2=AB=D0=BE=D1=81=D0=B8=D1=80=D0=BE=D1=82=D0=B5=D0=B2?= =?UTF-8?q?=D1=88=D0=B8=D0=B5=C2=BB=20=D0=B7=D0=BD=D0=B0=D1=87=D0=B5=D0=BD?= =?UTF-8?q?=D0=B8=D1=8F=20=D0=B2=20=D0=BC=D0=B5=D1=80=D0=B6=D0=B0=D1=85=20?= =?UTF-8?q?(=D0=BF=D1=80=D0=B8=D0=B7=D1=80=D0=B0=D1=87=D0=BD=D1=8B=D0=B5?= =?UTF-8?q?=20=D0=B7=D0=B0=D0=BD=D1=8F=D1=82=D0=B8=D1=8F)?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit В интерфейсе в объединённом блоке видно только верхне-левую ячейку; значения, оставшиеся в остальных ячейках мержа (занесли текст, а сверху наложили пустой мерж), в таблице не видны. Раньше парсер их отдавал: реальный кейс — ПИЭ-41БО, вторник, 1 пара, знаменатель (мерж AH14:AI15). По листу таких ячеек ровно одна; по 33 группам diff только этот слот. --- src/backend/schedule/xlsx.py | 8 ++++++++ tests/test_xlsx.py | 23 +++++++++++++++++++++++ 2 files changed, 31 insertions(+) diff --git a/src/backend/schedule/xlsx.py b/src/backend/schedule/xlsx.py index 01ed260..c960cfa 100644 --- a/src/backend/schedule/xlsx.py +++ b/src/backend/schedule/xlsx.py @@ -174,6 +174,14 @@ def _parse_sheet(xml_bytes: bytes, shared: list[str]) -> Worksheet: c2, r2 = _parse_ref(b) max_row = max(max_row, r2) max_col = max(max_col, c2) + # в объединённом блоке в интерфейсе видно только верхне-левую + # ячейку; «осиротевшие» значения в остальных (следы правок, + # например занесли текст, а сверху наложили мерж) — невидимые, + # их чистим, иначе парсер отдаёт «призрачные» занятия + for rr in range(r1, r2 + 1): + for cc in range(c1, c2 + 1): + if (rr, cc) != (r1, c1): + cells.pop((rr, cc), None) top = cells.get((r1, c1), "") if not top: continue diff --git a/tests/test_xlsx.py b/tests/test_xlsx.py index 3207769..135c973 100644 --- a/tests/test_xlsx.py +++ b/tests/test_xlsx.py @@ -8,6 +8,29 @@ import pytest from fixtures import fixture_sheets, fixture_xlsx, MAIN from schedule.errors import ScheduleError from schedule.xlsx import excel_serial_to_date, parse_workbook +from xlsx_builder import SheetSpec, build_workbook + + +def test_merge_orphan_value_cleared(): + """Значение в не-топ ячейке мержа в интерфейсе не видно — парсер его не отдаёт. + + Реальный кейс: в таблице занятие, а сверху на колонку группы наложили + пустой 2x2-мерж; в xlsx значение осталось, в таблице — пусто. + """ + spec = SheetSpec( + rows=[ + ["", "", "", ""], + ["т", "", "", ""], # A2 — топ мержа A2:B3 + ["", "осиротевшее", "", "призрак"], # B3, D3 + ], + merges=["A2:B3", "D2:D3"], + ) + sheets = parse_workbook(build_workbook({"s": spec})) + rows = sheets["s"].rows + # топ-значение разлилось по всему диапазону + assert rows[1][0] == rows[1][1] == rows[2][0] == rows[2][1] == "т" + # D2 пустое, D3 было «призрак» за мержом -> обе пусто + assert rows[1][3] == "" and rows[2][3] == "" def test_parse_workbook_fixture():