[AI]
Фикс создания файла
This commit is contained in:
@@ -2,8 +2,15 @@ import mistune
|
|||||||
from .style_registry import StyleRegistry
|
from .style_registry import StyleRegistry
|
||||||
from ..handlers import get_handler
|
from ..handlers import get_handler
|
||||||
|
|
||||||
|
import logging
|
||||||
|
|
||||||
|
logger = logging.getLogger(__name__)
|
||||||
|
|
||||||
|
log_handlers = set()
|
||||||
|
|
||||||
def render_markdown_to_docx(doc, md_content: str, images_dir, style_reg: StyleRegistry):
|
def render_markdown_to_docx(doc, md_content: str, images_dir, style_reg: StyleRegistry):
|
||||||
markdown = mistune.create_markdown(renderer=None)
|
# Включаем плагины: таблицы, переносы строк
|
||||||
|
markdown = mistune.create_markdown(renderer=None, plugins=['table'])
|
||||||
ast = markdown(md_content)
|
ast = markdown(md_content)
|
||||||
|
|
||||||
def walk(node):
|
def walk(node):
|
||||||
@@ -11,10 +18,25 @@ def render_markdown_to_docx(doc, md_content: str, images_dir, style_reg: StyleRe
|
|||||||
for item in node:
|
for item in node:
|
||||||
walk(item)
|
walk(item)
|
||||||
elif isinstance(node, dict):
|
elif isinstance(node, dict):
|
||||||
handler = get_handler(node["type"])
|
node_type = node["type"]
|
||||||
|
# Игнорируем вспомогательные узлы без логирования
|
||||||
|
if node_type in ("text", "blank_line", "table_head", "table_body", "table_row", "table_cell"):
|
||||||
|
if node.get("children"):
|
||||||
|
for child in node["children"]:
|
||||||
|
walk(child)
|
||||||
|
return
|
||||||
|
|
||||||
|
if node_type not in log_handlers:
|
||||||
|
logger.debug(f"Обрабатываем узел: {node_type}")
|
||||||
|
handler = get_handler(node_type)
|
||||||
if handler:
|
if handler:
|
||||||
|
if node_type not in log_handlers:
|
||||||
|
logger.debug(f" → найден обработчик для {node_type}")
|
||||||
handler(node, doc, images_dir, style_reg)
|
handler(node, doc, images_dir, style_reg)
|
||||||
elif node.get("children"):
|
elif node_type not in log_handlers:
|
||||||
|
logger.warning(f" → нет обработчика для {node_type}")
|
||||||
|
log_handlers.add(node_type)
|
||||||
|
if node.get("children"):
|
||||||
for child in node["children"]:
|
for child in node["children"]:
|
||||||
walk(child)
|
walk(child)
|
||||||
|
|
||||||
|
|||||||
@@ -13,4 +13,6 @@ def register_handler(node_type: str):
|
|||||||
return decorator
|
return decorator
|
||||||
|
|
||||||
def get_handler(node_type: str) -> Handler:
|
def get_handler(node_type: str) -> Handler:
|
||||||
return HANDLER_REGISTRY.get(node_type)
|
return HANDLER_REGISTRY.get(node_type)
|
||||||
|
|
||||||
|
from . import builtin
|
||||||
@@ -53,6 +53,7 @@ def _render_inline_tokens(p, tokens, doc, images_dir, style_reg):
|
|||||||
@register_handler("paragraph")
|
@register_handler("paragraph")
|
||||||
def handle_paragraph(node: dict, doc, images_dir: Path, style_reg):
|
def handle_paragraph(node: dict, doc, images_dir: Path, style_reg):
|
||||||
p = doc.add_paragraph()
|
p = doc.add_paragraph()
|
||||||
|
# mistune v3: node["children"] содержит [{"type": "text", "raw": "Текст"}, ...]
|
||||||
_render_inline_tokens(p, node["children"], doc, images_dir, style_reg)
|
_render_inline_tokens(p, node["children"], doc, images_dir, style_reg)
|
||||||
style_reg.apply_style(p.style, "Normal")
|
style_reg.apply_style(p.style, "Normal")
|
||||||
|
|
||||||
@@ -61,10 +62,13 @@ def handle_image(node: dict, doc, images_dir: Path, style_reg):
|
|||||||
# mistune v3: node["attrs"]["url"], alt в children
|
# mistune v3: node["attrs"]["url"], alt в children
|
||||||
# старый формат: node["src"], node["alt"]
|
# старый формат: node["src"], node["alt"]
|
||||||
src = node.get("attrs", {}).get("url") or node.get("src")
|
src = node.get("attrs", {}).get("url") or node.get("src")
|
||||||
# alt: mistune v3 → children[0].raw, старый → node["alt"]
|
|
||||||
alt_nodes = node.get("children", [])
|
alt_nodes = node.get("children", [])
|
||||||
alt = alt_nodes[0]["raw"] if alt_nodes else node.get("alt", "")
|
alt = alt_nodes[0]["raw"] if alt_nodes else node.get("alt", "")
|
||||||
|
|
||||||
|
# Убираем "images/" из src, если images_dir уже указывает на images/
|
||||||
|
if src.startswith("images/"):
|
||||||
|
src = src[7:] # "images/" = 7 символов
|
||||||
|
|
||||||
img_path = images_dir / src
|
img_path = images_dir / src
|
||||||
if img_path.exists():
|
if img_path.exists():
|
||||||
logger.debug(f"Обработка изображения: {img_path}, exists: {img_path.exists()}")
|
logger.debug(f"Обработка изображения: {img_path}, exists: {img_path.exists()}")
|
||||||
@@ -85,8 +89,8 @@ def handle_image(node: dict, doc, images_dir: Path, style_reg):
|
|||||||
cap_p = doc.add_paragraph(alt)
|
cap_p = doc.add_paragraph(alt)
|
||||||
style_reg.apply_style(cap_p.style, "Caption")
|
style_reg.apply_style(cap_p.style, "Caption")
|
||||||
else:
|
else:
|
||||||
doc.add_paragraph(f"[Изображение не найдено: {src}]")
|
|
||||||
logger.warning(f"[Изображение не найдено: {src}]")
|
logger.warning(f"[Изображение не найдено: {src}]")
|
||||||
|
doc.add_paragraph(f"[Изображение не найдено: {src}]")
|
||||||
|
|
||||||
@register_handler("list")
|
@register_handler("list")
|
||||||
def handle_list(node: dict, doc, images_dir: Path, style_reg):
|
def handle_list(node: dict, doc, images_dir: Path, style_reg):
|
||||||
@@ -98,28 +102,36 @@ def handle_list(node: dict, doc, images_dir: Path, style_reg):
|
|||||||
|
|
||||||
@register_handler("table")
|
@register_handler("table")
|
||||||
def handle_table(node: dict, doc, images_dir: Path, style_reg):
|
def handle_table(node: dict, doc, images_dir: Path, style_reg):
|
||||||
rows_data = node["children"] # [header_row, row1, row2...]
|
logger.debug(f"Обработка таблицы: {node}")
|
||||||
if not rows_data:
|
# mistune v3: table.children = [{"type": "table_head", ...}, {"type": "table_body", ...}]
|
||||||
return
|
header_node = node["children"][0] # table_head
|
||||||
|
body_nodes = node["children"][1:] # table_body...
|
||||||
|
|
||||||
header_row = rows_data[0]["children"]
|
# header_node.children = [table_cell, table_cell...]
|
||||||
body_rows = rows_data[1:]
|
header_cells = header_node["children"]
|
||||||
|
num_cols = len(header_cells)
|
||||||
|
|
||||||
num_cols = len(header_row)
|
|
||||||
table = doc.add_table(rows=1, cols=num_cols)
|
table = doc.add_table(rows=1, cols=num_cols)
|
||||||
table.style = "Table Grid" # можно настроить в YAML
|
table.style = "Table Grid"
|
||||||
|
|
||||||
# Заполняем заголовок
|
# Заполняем заголовки
|
||||||
hdr_cells = table.rows[0].cells
|
hdr_cells = table.rows[0].cells
|
||||||
for i, cell_data in enumerate(header_row):
|
for i, cell_node in enumerate(header_cells):
|
||||||
if i < num_cols:
|
if i < num_cols:
|
||||||
cell = hdr_cells[i]
|
cell = hdr_cells[i]
|
||||||
_render_inline_tokens(cell.paragraphs[0], cell_data["children"], doc, images_dir, style_reg)
|
logger.debug(f"cell_node[{i}] = {cell_node}")
|
||||||
|
# cell_node = table_cell, его children = [text, ...]
|
||||||
|
children = cell_node.get("children", [])
|
||||||
|
logger.debug(f"Заголовок ячейки {i}: children = {children}")
|
||||||
|
_render_inline_tokens(cell.paragraphs[0], children, doc, images_dir, style_reg)
|
||||||
|
|
||||||
# Заполняем тело
|
# Заполняем тело
|
||||||
for row_data in body_rows:
|
for body_node in body_nodes:
|
||||||
cells = table.add_row().cells
|
for row_node in body_node["children"]: # table_row
|
||||||
for i, cell_data in enumerate(row_data["children"]):
|
cells = table.add_row().cells
|
||||||
if i < num_cols:
|
for i, cell_node in enumerate(row_node["children"]): # table_cell
|
||||||
cell = cells[i]
|
if i < num_cols:
|
||||||
_render_inline_tokens(cell.paragraphs[0], cell_data["children"], doc, images_dir, style_reg)
|
cell = cells[i]
|
||||||
|
children = cell_node.get("children", [])
|
||||||
|
logger.debug(f"Ячейка {i}: children = {children}")
|
||||||
|
_render_inline_tokens(cell.paragraphs[0], children, doc, images_dir, style_reg)
|
||||||
|
|||||||
@@ -42,7 +42,7 @@ def create_gost_example():
|
|||||||
import shutil
|
import shutil
|
||||||
shutil.rmtree(doc_dir.parent, ignore_errors=True)
|
shutil.rmtree(doc_dir.parent, ignore_errors=True)
|
||||||
|
|
||||||
def test_gost_example_document_not_empty(create_gost_example):
|
def test_gost_example_document_is_not_empty(create_gost_example):
|
||||||
input_dir = create_gost_example
|
input_dir = create_gost_example
|
||||||
|
|
||||||
process_document(
|
process_document(
|
||||||
@@ -54,11 +54,100 @@ def test_gost_example_document_not_empty(create_gost_example):
|
|||||||
output_path = Path("tests/output/gost_example.docx")
|
output_path = Path("tests/output/gost_example.docx")
|
||||||
assert output_path.exists(), "Файл .docx не создан"
|
assert output_path.exists(), "Файл .docx не создан"
|
||||||
|
|
||||||
# Открываем и проверяем, что в документе есть хотя бы 1 параграф
|
# Открываем и проверяем, что в документе есть хотя бы 1 непустой параграф
|
||||||
doc = Document(output_path)
|
doc = Document(output_path)
|
||||||
assert len(doc.paragraphs) > 0, "Документ пустой (нет параграфов)"
|
|
||||||
assert len(doc.tables) >= 0, "Документ не содержит таблиц (это нормально)"
|
# Проверяем, что есть хотя бы 1 параграф
|
||||||
assert any(p.text.strip() != "" for p in doc.paragraphs), "Все параграфы пустые"
|
assert len(doc.paragraphs) > 0, f"Документ пустой: {len(doc.paragraphs)} параграфов"
|
||||||
|
|
||||||
|
# Проверяем, что хотя бы один параграф **не пустой**
|
||||||
|
non_empty_paragraphs = [p for p in doc.paragraphs if p.text.strip() != ""]
|
||||||
|
assert len(non_empty_paragraphs) > 0, f"Все параграфы пустые: {[p.text for p in doc.paragraphs]}"
|
||||||
|
|
||||||
|
# Проверяем, что есть хотя бы один заголовок "ВВЕДЕНИЕ"
|
||||||
|
found_intro = any("ВВЕДЕНИЕ" in p.text for p in doc.paragraphs)
|
||||||
|
assert found_intro, f"Заголовок 'ВВЕДЕНИЕ' не найден. Параграфы: {[p.text for p in doc.paragraphs]}"
|
||||||
|
|
||||||
|
# Проверяем, что есть текст "Текст введения"
|
||||||
|
found_intro_text = any("Текст введения" in p.text for p in doc.paragraphs)
|
||||||
|
assert found_intro_text, f"Текст 'Текст введения' не найден. Параграфы: {[p.text for p in doc.paragraphs]}"
|
||||||
|
|
||||||
|
# Удаляем после теста
|
||||||
|
output_path.unlink(missing_ok=True)
|
||||||
|
|
||||||
|
|
||||||
|
def test_gost_example_document_content(create_gost_example):
|
||||||
|
input_dir = create_gost_example
|
||||||
|
|
||||||
|
process_document(
|
||||||
|
input_dir=input_dir,
|
||||||
|
output_dir=Path("tests/output"),
|
||||||
|
style_config_path=Path("resources/themes/gost.yaml"),
|
||||||
|
)
|
||||||
|
|
||||||
|
output_path = Path("tests/output/gost_example.docx")
|
||||||
|
assert output_path.exists(), "Файл .docx не создан"
|
||||||
|
|
||||||
|
doc = Document(output_path)
|
||||||
|
|
||||||
|
# Проверим, что есть хотя бы 2 параграфа (заголовок + текст)
|
||||||
|
assert len(doc.paragraphs) >= 2, f"Документ содержит только {len(doc.paragraphs)} параграфов"
|
||||||
|
|
||||||
|
# Проверим, что в параграфах есть нужный текст
|
||||||
|
texts = [p.text for p in doc.paragraphs]
|
||||||
|
assert "ВВЕДЕНИЕ" in texts, "Заголовок 'ВВЕДЕНИЕ' не найден"
|
||||||
|
assert "Текст введения." in texts, "Текст введения не найден"
|
||||||
|
assert "Текст актуальности." in texts, "Текст актуальности не найден"
|
||||||
|
|
||||||
|
# Проверим, что таблица добавлена
|
||||||
|
assert len(doc.tables) >= 1, "Таблица не найдена в документе"
|
||||||
|
|
||||||
|
# Проверим, что в таблице есть нужные данные
|
||||||
|
table = doc.tables[0]
|
||||||
|
assert table.cell(0, 0).text == "Показатель"
|
||||||
|
assert table.cell(0, 1).text == "2022" # заголовок
|
||||||
|
assert table.cell(1, 0).text == "Выручка"
|
||||||
|
assert table.cell(1, 1).text == "1000" # тело таблицы
|
||||||
|
|
||||||
|
# Удаляем после теста
|
||||||
|
output_path.unlink(missing_ok=True)
|
||||||
|
|
||||||
|
def test_gost_example_document_is_not_empty_debug(create_gost_example):
|
||||||
|
input_dir = create_gost_example
|
||||||
|
|
||||||
|
process_document(
|
||||||
|
input_dir=input_dir,
|
||||||
|
output_dir=Path("tests/output"),
|
||||||
|
style_config_path=Path("resources/themes/gost.yaml"),
|
||||||
|
)
|
||||||
|
|
||||||
|
output_path = Path("tests/output/gost_example.docx")
|
||||||
|
assert output_path.exists(), "Файл .docx не создан"
|
||||||
|
|
||||||
|
# Открываем и проверяем, что в документе есть хотя бы 1 непустой параграф
|
||||||
|
doc = Document(output_path)
|
||||||
|
|
||||||
|
print(f"Количество параграфов: {len(doc.paragraphs)}")
|
||||||
|
for i, p in enumerate(doc.paragraphs):
|
||||||
|
print(f"Параграф {i}: '{p.text}' (len={len(p.text)})")
|
||||||
|
print(f" Количество runs: {len(p.runs)}")
|
||||||
|
for j, run in enumerate(p.runs):
|
||||||
|
print(f" Run {j}: '{run.text}' (len={len(run.text)})")
|
||||||
|
|
||||||
|
# Проверяем, что есть хотя бы 1 параграф
|
||||||
|
assert len(doc.paragraphs) > 0, f"Документ пустой: {len(doc.paragraphs)} параграфов"
|
||||||
|
|
||||||
|
# Проверяем, что хотя бы один параграф **не пустой**
|
||||||
|
non_empty_paragraphs = [p for p in doc.paragraphs if p.text.strip() != ""]
|
||||||
|
assert len(non_empty_paragraphs) > 0, f"Все параграфы пустые: {[p.text for p in doc.paragraphs]}"
|
||||||
|
|
||||||
|
# Проверяем, что есть хотя бы один заголовок "ВВЕДЕНИЕ"
|
||||||
|
found_intro = any("ВВЕДЕНИЕ" in p.text for p in doc.paragraphs)
|
||||||
|
assert found_intro, f"Заголовок 'ВВЕДЕНИЕ' не найден. Параграфы: {[p.text for p in doc.paragraphs]}"
|
||||||
|
|
||||||
|
# Проверяем, что есть текст "Текст введения"
|
||||||
|
found_intro_text = any("Текст введения" in p.text for p in doc.paragraphs)
|
||||||
|
assert found_intro_text, f"Текст 'Текст введения' не найден. Параграфы: {[p.text for p in doc.paragraphs]}"
|
||||||
|
|
||||||
# Удаляем после теста
|
# Удаляем после теста
|
||||||
output_path.unlink(missing_ok=True)
|
output_path.unlink(missing_ok=True)
|
||||||
@@ -28,7 +28,7 @@ def test_handle_image():
|
|||||||
doc = Document()
|
doc = Document()
|
||||||
style_reg = StyleRegistry(Path("resources/style_config.yaml"))
|
style_reg = StyleRegistry(Path("resources/style_config.yaml"))
|
||||||
style_reg.ensure_styles_in_doc(doc)
|
style_reg.ensure_styles_in_doc(doc)
|
||||||
node = {"type": "image", "src": "images/logo.png", "alt": "Логотип"} # ✅ src = images/logo.png
|
node = {"type": "image", "src": "images/logo.png", "alt": "Логотип"}
|
||||||
|
|
||||||
# Создаем реальное изображение
|
# Создаем реальное изображение
|
||||||
img_path = Path("tests/fixtures/images/logo.png")
|
img_path = Path("tests/fixtures/images/logo.png")
|
||||||
@@ -36,7 +36,8 @@ def test_handle_image():
|
|||||||
img = Image.new("RGB", (100, 100), color="red")
|
img = Image.new("RGB", (100, 100), color="red")
|
||||||
img.save(img_path)
|
img.save(img_path)
|
||||||
|
|
||||||
handle_image(node, doc, Path("tests/fixtures"), style_reg) # images_dir = tests/fixtures
|
# images_dir должен указывать на папку images/
|
||||||
|
handle_image(node, doc, Path("tests/fixtures/images"), style_reg)
|
||||||
|
|
||||||
# После вызова должно быть 2 параграфа: картинка и подпись
|
# После вызова должно быть 2 параграфа: картинка и подпись
|
||||||
assert len(doc.paragraphs) == 2
|
assert len(doc.paragraphs) == 2
|
||||||
@@ -93,18 +94,23 @@ def test_handle_table():
|
|||||||
node = {
|
node = {
|
||||||
"type": "table",
|
"type": "table",
|
||||||
"children": [
|
"children": [
|
||||||
{ # header
|
{ # table_head
|
||||||
"type": "table_row",
|
"type": "table_head",
|
||||||
"children": [
|
"children": [
|
||||||
{"type": "table_cell", "children": [{"type": "text", "raw": "Заголовок 1"}]},
|
{"type": "table_cell", "children": [{"type": "text", "raw": "Заголовок 1"}]},
|
||||||
{"type": "table_cell", "children": [{"type": "text", "raw": "Заголовок 2"}]},
|
{"type": "table_cell", "children": [{"type": "text", "raw": "Заголовок 2"}]},
|
||||||
]
|
]
|
||||||
},
|
},
|
||||||
{ # row 1
|
{ # table_body
|
||||||
"type": "table_row",
|
"type": "table_body",
|
||||||
"children": [
|
"children": [
|
||||||
{"type": "table_cell", "children": [{"type": "text", "raw": "Ячейка 1"}]},
|
{ # table_row
|
||||||
{"type": "table_cell", "children": [{"type": "text", "raw": "Ячейка 2"}]},
|
"type": "table_row",
|
||||||
|
"children": [
|
||||||
|
{"type": "table_cell", "children": [{"type": "text", "raw": "Ячейка 1"}]},
|
||||||
|
{"type": "table_cell", "children": [{"type": "text", "raw": "Ячейка 2"}]},
|
||||||
|
]
|
||||||
|
}
|
||||||
]
|
]
|
||||||
}
|
}
|
||||||
]
|
]
|
||||||
@@ -124,22 +130,32 @@ def test_handle_table_with_br():
|
|||||||
node = {
|
node = {
|
||||||
"type": "table",
|
"type": "table",
|
||||||
"children": [
|
"children": [
|
||||||
{ # header
|
{ # table_head
|
||||||
"type": "table_row",
|
"type": "table_head",
|
||||||
"children": [
|
"children": [
|
||||||
{"type": "table_cell", "children": [{"type": "text", "raw": "Заголовок 1"}]},
|
{ # table_row
|
||||||
{"type": "table_cell", "children": [{"type": "text", "raw": "Заголовок 2"}]},
|
"type": "table_row",
|
||||||
|
"children": [
|
||||||
|
{"type": "table_cell", "children": [{"type": "text", "raw": "Заголовок 1"}]},
|
||||||
|
{"type": "table_cell", "children": [{"type": "text", "raw": "Заголовок 2"}]},
|
||||||
|
]
|
||||||
|
}
|
||||||
]
|
]
|
||||||
},
|
},
|
||||||
{ # row 1
|
{ # table_body
|
||||||
"type": "table_row",
|
"type": "table_body",
|
||||||
"children": [
|
"children": [
|
||||||
{"type": "table_cell", "children": [
|
{ # row 1
|
||||||
{"type": "text", "raw": "Первая строка"},
|
"type": "table_row",
|
||||||
{"type": "softbreak"}, # или linebreak/html
|
"children": [
|
||||||
{"type": "text", "raw": "Вторая строка"}
|
{"type": "table_cell", "children": [
|
||||||
]},
|
{"type": "text", "raw": "Первая строка"},
|
||||||
{"type": "table_cell", "children": [{"type": "text", "raw": "Ячейка 2"}]},
|
{"type": "softbreak"}, # или linebreak/html
|
||||||
|
{"type": "text", "raw": "Вторая строка"}
|
||||||
|
]},
|
||||||
|
{"type": "table_cell", "children": [{"type": "text", "raw": "Ячейка 2"}]},
|
||||||
|
]
|
||||||
|
}
|
||||||
]
|
]
|
||||||
}
|
}
|
||||||
]
|
]
|
||||||
|
|||||||
53
tests/test_real_cli_call.py
Normal file
53
tests/test_real_cli_call.py
Normal file
@@ -0,0 +1,53 @@
|
|||||||
|
import subprocess
|
||||||
|
import sys
|
||||||
|
from pathlib import Path
|
||||||
|
|
||||||
|
def test_real_cli_call_creates_non_empty_doc():
|
||||||
|
import tempfile
|
||||||
|
|
||||||
|
with tempfile.TemporaryDirectory() as tmp_dir:
|
||||||
|
tmp_path = Path(tmp_dir)
|
||||||
|
input_dir = tmp_path / "inputs"
|
||||||
|
output_dir = tmp_path / "outputs"
|
||||||
|
input_dir.mkdir()
|
||||||
|
output_dir.mkdir()
|
||||||
|
|
||||||
|
# Создаём папку example_doc
|
||||||
|
doc_dir = input_dir / "example_doc"
|
||||||
|
doc_dir.mkdir()
|
||||||
|
(doc_dir / "document.md").write_text("# ВВЕДЕНИЕ\n\nТекст введения.", encoding="utf-8")
|
||||||
|
(doc_dir / "images").mkdir()
|
||||||
|
|
||||||
|
# Запускаем CLI через subprocess
|
||||||
|
result = subprocess.run([
|
||||||
|
sys.executable, "-m", "src",
|
||||||
|
"--input", str(input_dir),
|
||||||
|
"--output", str(output_dir),
|
||||||
|
"--style", "resources/style_config.yaml"
|
||||||
|
], capture_output=True, text=True, cwd=Path(__file__).parent.parent) # запускаем из корня проекта
|
||||||
|
|
||||||
|
print("STDOUT:", result.stdout)
|
||||||
|
print("STDERR:", result.stderr)
|
||||||
|
print("Return code:", result.returncode)
|
||||||
|
|
||||||
|
assert result.returncode == 0, f"CLI завершился с ошибкой: {result.stderr}"
|
||||||
|
|
||||||
|
# Проверяем, что файл создан
|
||||||
|
output_path = output_dir / "example_doc.docx"
|
||||||
|
assert output_path.exists(), "Файл .docx не создан через CLI"
|
||||||
|
|
||||||
|
# Открываем и проверяем
|
||||||
|
from docx import Document
|
||||||
|
doc = Document(output_path)
|
||||||
|
|
||||||
|
print(f"Real CLI: Количество параграфов: {len(doc.paragraphs)}")
|
||||||
|
for i, p in enumerate(doc.paragraphs):
|
||||||
|
print(f"Real CLI: Параграф {i}: '{p.text}' (len={len(p.text)})")
|
||||||
|
|
||||||
|
# Проверяем, что есть хотя бы 1 непустой параграф
|
||||||
|
non_empty_paragraphs = [p for p in doc.paragraphs if p.text.strip() != ""]
|
||||||
|
assert len(non_empty_paragraphs) > 0, f"Real CLI: Все параграфы пустые: {[p.text for p in doc.paragraphs]}"
|
||||||
|
|
||||||
|
# Проверяем, что есть заголовок
|
||||||
|
found_intro = any("ВВЕДЕНИЕ" in p.text for p in doc.paragraphs)
|
||||||
|
assert found_intro, f"Real CLI: Заголовок 'ВВЕДЕНИЕ' не найден"
|
||||||
Reference in New Issue
Block a user