"""Загрузка исходных данных: Таблица 1 (КИ), Таблица 2, Таблица 3, индекс MIF-файлов.""" import zipfile from pathlib import Path from xml.etree import ElementTree as ET from openpyxl import load_workbook def load_ki(path: str) -> dict: """Таблица 1: {название_поля → значение}.""" wb = load_workbook(path, data_only=True) ws = wb.active ki = {} for row in ws.iter_rows(values_only=True): if row[0] is not None and row[1] is not None: ki[str(row[0]).strip()] = str(row[1]).strip() return ki def ki_val(ki: dict, keyword: str) -> str: """Найти значение в словаре КИ по подстроке ключа (без учёта регистра).""" for k, v in ki.items(): if keyword.lower() in k.lower(): return v return "" def load_table2(path: str) -> list: """Таблица 2: список строк (кортежи). Заголовок и пустые строки пропускаются.""" wb = load_workbook(path, data_only=True) ws = wb.active rows = [] for i, row in enumerate(ws.iter_rows(values_only=True)): if i == 0: continue if all(v is None for v in row[:5]): continue rows.append(row) return rows def build_mif_index(mif_dir: str) -> dict: """Индекс MIF-файлов: stem → полный путь.""" index = {} for p in Path(mif_dir).rglob("*.mif"): index[p.stem] = str(p) return index def load_table3(path: str): """ Таблица 3 (для схем): возвращает два словаря (images, scales). images: {zu_name → PNG-байты} scales: {zu_name → строка масштаба, например 'Масштаб: 1:150'} Структура xlsx: каждый блок = 3 строки. Строка 1: ('вид он', None, None, 'зу', 'название файла', zu_name) Строка 2: изображение якорено здесь Строка 3: масштаб в столбце A (c1) Изображения извлекаются прямо из zip-архива xlsx через drawing/rels. """ # --- Читаем имена ZU и масштабы по строкам (1-based) --- wb = load_workbook(path, data_only=True) ws = wb.active zu_by_row = {} # 1-based строка заголовка → zu_name scales = {} # zu_name → масштаб (строка из c1 третьей строки блока) for i, row in enumerate(ws.iter_rows(values_only=True), start=1): zu_name = row[5] if len(row) > 5 else None if zu_name and str(zu_name).strip().startswith("выявл"): zu_name = str(zu_name).strip() zu_by_row[i] = zu_name # масштаб — третья строка блока (header+2) scale_val = ws.cell(i + 2, 1).value if scale_val: scales[zu_name] = str(scale_val).strip() # --- Читаем привязку картинок из drawing XML --- _R = "http://schemas.openxmlformats.org/officeDocument/2006/relationships" _XDR = "http://schemas.openxmlformats.org/drawingml/2006/spreadsheetDrawing" _A = "http://schemas.openxmlformats.org/drawingml/2006/main" ns = {"xdr": _XDR, "a": _A, "r": _R} result = {} with zipfile.ZipFile(path, "r") as z: names = z.namelist() # Найдём все drawing-файлы drawing_files = [n for n in names if n.startswith("xl/drawings/drawing") and n.endswith(".xml")] for draw_path in drawing_files: rels_path = draw_path.replace("xl/drawings/", "xl/drawings/_rels/") + ".rels" if rels_path not in names: continue rels_root = ET.fromstring(z.read(rels_path)) rid_to_media = { rel.get("Id"): rel.get("Target").split("/")[-1] for rel in rels_root } draw_root = ET.fromstring(z.read(draw_path)) for anchor in draw_root: from_el = anchor.find(f"{{{_XDR}}}from") if from_el is None: continue row_0b = int(from_el.find(f"{{{_XDR}}}row").text) # 0-based img_row_1b = row_0b + 1 # 1-based blip = anchor.find(f".//{{{_A}}}blip") if blip is None: continue rid = blip.get(f"{{{_R}}}embed") media_filename = rid_to_media.get(rid) if not media_filename: continue media_path = f"xl/media/{media_filename}" if media_path not in names: continue # Ищем ближайшую ZU-строку заголовка, которая <= img_row_1b header_row = max( (r for r in zu_by_row if r <= img_row_1b), default=None, ) if header_row is None: continue zu_name = zu_by_row[header_row] result[zu_name] = z.read(media_path) return result, scales