139 lines
5.4 KiB
Python
139 lines
5.4 KiB
Python
"""
|
||
Однократная/повторная генерация 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 _is_num_like(s: str) -> bool:
|
||
if not isinstance(s, str):
|
||
return False
|
||
t = s.strip()
|
||
if not t:
|
||
return False
|
||
# 1, 1., 1.0
|
||
return t.isdigit() or t.replace(".", "", 1).isdigit()
|
||
|
||
|
||
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
|
||
|
||
# Заголовок верхнего ряда:
|
||
# в новых версиях файла набор колонок может отличаться, поэтому
|
||
# распознаём динамически: строка с номерами + следующая строка с подписями
|
||
# в тех же колонках.
|
||
cols = [c[0] for c in cells]
|
||
vals = [c[1] for c in cells]
|
||
if len(cells) >= 4 and all(_is_num_like(v) for v in vals):
|
||
r2 = r + 1
|
||
cells2 = _row_cells(ws, r2)
|
||
map2 = {c: v for c, v in cells2}
|
||
if all(c in map2 and str(map2[c]).strip() for c in cols):
|
||
labels = [str(map2[c]).strip() for c in cols]
|
||
# Если в следующей строке снова только числа — это не шапка.
|
||
if not all(_is_num_like(v) for v in labels):
|
||
nums = [str(v).strip() for v in vals]
|
||
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()
|