시나리오 내용 반영

This commit is contained in:
KIM SU BO committed 2026-10-08 16:43:48 +09:00
1 parent d9287de729
commit 350bb92bf9
270 files changed
+198867

No files matched your search

+220
View File
@@ -0,0 +1,220 @@
import os
import re
import sys
import shutil
from pathlib import Path
from docx import Document
from docx.oxml.text.paragraph import CT_P
from docx.oxml.table import CT_Tbl
from docx.text.paragraph import Paragraph
from docx.table import Table
def clean_cell_text(cell):
"""Clean and format table cell text into markdown-compatible single line."""
# Process cell paragraphs
lines = []
for p in cell.paragraphs:
p_text = format_paragraph(p).strip()
if p_text:
lines.append(p_text)
full_text = "<br>".join(lines)
# Escape pipes
full_text = full_text.replace("|", r"\|")
# Clean excessive whitespace
full_text = re.sub(r"[ \t]+", " ", full_text).strip()
return full_text
def format_paragraph(p):
"""Format paragraph runs into markdown text with bold/italic."""
runs_text = []
for r in p.runs:
text = r.text
if not text:
continue
# Check formatting
is_bold = bool(r.bold)
is_italic = bool(r.italic)
# Don't format pure whitespace runs
if text.strip() == "":
runs_text.append(text)
continue
prefix_space = " " if text.startswith(" ") else ""
suffix_space = " " if text.endswith(" ") else ""
core_text = text.strip()
if is_bold and is_italic:
core_text = f"***{core_text}***"
elif is_bold:
core_text = f"**{core_text}**"
elif is_italic:
core_text = f"*{core_text}*"
runs_text.append(prefix_space + core_text + suffix_space)
res = "".join(runs_text)
if not res.strip() and p.text.strip():
# Fallback if runs were empty or weird
res = p.text.strip()
return res
def convert_paragraph_to_md(p):
"""Convert a docx Paragraph to Markdown string."""
text = format_paragraph(p).strip()
if not text:
return ""
style_name = p.style.name if p.style and p.style.name else ""
style_lower = style_name.lower()
# Check heading styles
if "heading 1" in style_lower or style_name in ["제목 1", "Title", "제목"]:
return f"# {text}\n"
elif "heading 2" in style_lower or style_name in ["제목 2"]:
return f"## {text}\n"
elif "heading 3" in style_lower or style_name in ["제목 3", "Subtitle", "부제목"]:
return f"### {text}\n"
elif "heading 4" in style_lower or style_name in ["제목 4"]:
return f"#### {text}\n"
elif "heading 5" in style_lower or style_name in ["제목 5"]:
return f"##### {text}\n"
elif "heading 6" in style_lower or style_name in ["제목 6"]:
return f"###### {text}\n"
# Check list styles
if "bullet" in style_lower or "글머리" in style_lower:
return f"- {text}"
if "number" in style_lower or "번호" in style_lower:
return f"1. {text}"
return text
def convert_table_to_md(table):
"""Convert a docx Table to Markdown table format."""
if not table.rows:
return ""
rows_data = []
max_cols = 0
for row in table.rows:
row_cells = []
for cell in row.cells:
cell_text = clean_cell_text(cell)
row_cells.append(cell_text)
max_cols = max(max_cols, len(row_cells))
rows_data.append(row_cells)
if max_cols == 0:
return ""
# Standardize column count
norm_rows = []
for row in rows_data:
if len(row) < max_cols:
row.extend([""] * (max_cols - len(row)))
elif len(row) > max_cols:
row = row[:max_cols]
norm_rows.append(row)
if not norm_rows:
return ""
md_lines = []
# If table has only 1 row, format as key-value or 1-row table with header
if len(norm_rows) == 1:
# 1-row table: row 0 as header or data row
header = norm_rows[0]
md_lines.append("| " + " | ".join(header) + " |")
md_lines.append("| " + " | ".join(["---"] * max_cols) + " |")
else:
# Header row
header = norm_rows[0]
md_lines.append("| " + " | ".join(header) + " |")
md_lines.append("| " + " | ".join(["---"] * max_cols) + " |")
# Data rows
for row in norm_rows[1:]:
md_lines.append("| " + " | ".join(row) + " |")
return "\n".join(md_lines) + "\n"
def convert_docx_file(docx_path):
"""Read a docx file and convert its contents to markdown."""
doc = Document(docx_path)
md_blocks = []
for block in doc.element.body:
if isinstance(block, CT_P):
p = Paragraph(block, doc)
md_text = convert_paragraph_to_md(p)
if md_text:
md_blocks.append(md_text)
elif isinstance(block, CT_Tbl):
t = Table(block, doc)
md_table = convert_table_to_md(t)
if md_table:
md_blocks.append(md_table)
content = "\n\n".join(md_blocks)
# Normalize consecutive empty lines
content = re.sub(r"\n{3,}", "\n\n", content).strip() + "\n"
return content
def run_conversion(src_dir="scenario_docx", dst_dir="scenario_md"):
"""Convert all docx files in src_dir to md files in dst_dir, copying existing md files."""
src_path = Path(src_dir)
dst_path = Path(dst_dir)
dst_path.mkdir(parents=True, exist_ok=True)
converted_count = 0
copied_count = 0
error_count = 0
for root, dirs, files in os.walk(src_path):
rel_dir = Path(root).relative_to(src_path)
target_dir = dst_path / rel_dir
target_dir.mkdir(parents=True, exist_ok=True)
for file_name in files:
src_file = Path(root) / file_name
ext = src_file.suffix.lower()
if ext == ".docx":
target_file_name = src_file.stem + ".md"
target_file = target_dir / target_file_name
try:
md_content = convert_docx_file(str(src_file))
with open(target_file, "w", encoding="utf-8") as f:
f.write(md_content)
converted_count += 1
except Exception as e:
print(f"[ERROR] Failed to convert {src_file}: {e}", file=sys.stderr)
error_count += 1
elif ext == ".md":
target_file = target_dir / file_name
try:
shutil.copy2(src_file, target_file)
copied_count += 1
except Exception as e:
print(f"[ERROR] Failed to copy {src_file}: {e}", file=sys.stderr)
error_count += 1
else:
# Other files (e.g. assets, images)
target_file = target_dir / file_name
try:
shutil.copy2(src_file, target_file)
copied_count += 1
except Exception as e:
print(f"[ERROR] Failed to copy {src_file}: {e}", file=sys.stderr)
error_count += 1
print("=" * 60)
print("Conversion Summary:")
print(f" Converted .docx -> .md : {converted_count}")
print(f" Copied .md / others : {copied_count}")
print(f" Errors : {error_count}")
print("=" * 60)
if __name__ == "__main__":
run_conversion()