""" Однократная/повторная генерация criteria_interface_layout.json из Excel «Запрос для ИИ Общий(2).xlsx» (листы с «интерфейс» в названии). Запуск (путь к xlsx можно передать аргументом): py extract_criteria_interface_layout.py "C:\\Users\\...\\Запрос для ИИ Общий(2).xlsx" """ from __future__ import annotations import json import os import sys import openpyxl SHEET_TO_SHIPPING_TYPES: dict[str, list[str]] = { "Авто LTL интерфейс": ["Автомобильная перевозка (LTL)"], "Авто FTL интерфейс": ["Автомобильная перевозка (FTL)"], "Море, море+жд, жд LCL интерфейc": [ "Морская перевозка (LCL)", "Железнодорожная перевозка (LCL)", "Мультимодальная перевозка море + ж/д (LCL)", ], "Море, море+жд, жд FCL интерфейc": [ "Морская перевозка (FCL)", "Железнодорожная перевозка (FCL)", "Мультимодальная перевозка море + ж/д (FCL)", ], "Авиа Интерфейс": ["Авиаперевозка"], } def _row_cells(ws, r: int, max_c: int = 12) -> list[tuple[int, str]]: out: list[tuple[int, str]] = [] for c in range(1, max_c + 1): v = ws.cell(r, c).value if v is not None and str(v).strip(): out.append((c, str(v).strip())) return out def parse_interface_sheet(ws) -> list[dict]: """Разбор листа интерфейса в последовательность визуальных блоков.""" blocks: list[dict] = [] r = 1 max_r = ws.max_row or 1 while r <= max_r: cells = _row_cells(ws, r) if not cells: r += 1 continue # Заголовок: 7 ячеек в колонках 1,2,3,4,5,7,9 и все значения — «номера пунктов» if ( len(cells) == 7 and {c[0] for c in cells} == {1, 2, 3, 4, 5, 7, 9} and all(str(c[1]).strip().isdigit() or c[1].strip().replace(".", "").isdigit() for c in cells) ): nums = [c[1] for c in sorted(cells, key=lambda x: (x[0] not in (1, 2, 3, 4, 5, 7, 9), x[0]))] # порядок как в Excel слева направо по колонкам nums = [c[1] for c in sorted(cells, key=lambda x: x[0])] r2 = r + 1 cells2 = _row_cells(ws, r2) if len(cells2) == 7 and {c[0] for c in cells2} == {1, 2, 3, 4, 5, 7, 9}: labels = [c[1] for c in sorted(cells2, key=lambda x: x[0])] blocks.append({"type": "header", "nums": nums, "labels": labels}) r += 2 continue # Только правая пара (6 = номер, 7 = текст) — например «20 Место таможни» if len(cells) == 2 and cells[0][0] == 6 and cells[1][0] == 7: blocks.append({"type": "right_67", "num": cells[0][1], "label": cells[1][1]}) r += 1 continue # Две пары: 1-2 и 6-7 if len(cells) == 4 and {cells[i][0] for i in range(4)} == {1, 2, 6, 7}: blocks.append( { "type": "pair", "left": {"num": cells[0][1], "label": cells[1][1]}, "right": {"num": cells[2][1], "label": cells[3][1]}, } ) r += 1 continue # Только левая пара 1-2 (один блок на строку) if len(cells) == 2 and cells[0][0] == 1 and cells[1][0] == 2: blocks.append({"type": "left_12", "num": cells[0][1], "label": cells[1][1]}) r += 1 continue r += 1 return blocks def main() -> None: xlsx = ( sys.argv[1] if len(sys.argv) > 1 else os.path.join(os.path.dirname(__file__), "Запрос для ИИ Общий(3).xlsx") ) if not os.path.isfile(xlsx): print("Файл не найден:", xlsx) sys.exit(1) wb = openpyxl.load_workbook(xlsx, read_only=False, data_only=True) out: dict = {"version": 1, "source_xlsx": os.path.basename(xlsx), "by_shipping_type": {}} for sheet_name, type_names in SHEET_TO_SHIPPING_TYPES.items(): if sheet_name not in wb.sheetnames: print("Пропуск: лист не найден:", sheet_name) continue blocks = parse_interface_sheet(wb[sheet_name]) for tn in type_names: out["by_shipping_type"][tn] = {"sheet": sheet_name, "blocks": blocks} out_path = os.path.join(os.path.dirname(__file__), "criteria_interface_layout.json") with open(out_path, "w", encoding="utf-8") as f: json.dump(out, f, ensure_ascii=False, indent=2) print("Записано:", out_path, "типов:", len(out["by_shipping_type"])) if __name__ == "__main__": main()