131 lines
5.2 KiB
Python
131 lines
5.2 KiB
Python
"""Загрузка исходных данных: Таблица 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
|