nlp-project / scripts /build_final_report_docx.py
ervua's picture
Deploy Turkish Legal RAG App
6dfa658
Raw
History Blame Contribute Delete
6.14 kB
from __future__ import annotations
import re
from pathlib import Path
from docx import Document
from docx.enum.text import WD_ALIGN_PARAGRAPH
from docx.enum.table import WD_TABLE_ALIGNMENT, WD_CELL_VERTICAL_ALIGNMENT
from docx.oxml import OxmlElement
from docx.oxml.ns import qn
from docx.shared import Inches, Pt, RGBColor
ROOT = Path(__file__).resolve().parents[1]
SOURCE = ROOT / "docs" / "final_report_draft.md"
OUTPUT = ROOT / "deliverables" / "Turkish_Legal_RAG_Final_Report.docx"
def set_cell_shading(cell, fill: str) -> None:
tc_pr = cell._tc.get_or_add_tcPr()
shd = OxmlElement("w:shd")
shd.set(qn("w:fill"), fill)
tc_pr.append(shd)
def set_cell_margins(cell, top=80, start=120, bottom=80, end=120) -> None:
tc = cell._tc
tc_pr = tc.get_or_add_tcPr()
tc_mar = tc_pr.first_child_found_in("w:tcMar")
if tc_mar is None:
tc_mar = OxmlElement("w:tcMar")
tc_pr.append(tc_mar)
for margin, value in [("top", top), ("start", start), ("bottom", bottom), ("end", end)]:
node = tc_mar.find(qn(f"w:{margin}"))
if node is None:
node = OxmlElement(f"w:{margin}")
tc_mar.append(node)
node.set(qn("w:w"), str(value))
node.set(qn("w:type"), "dxa")
def style_document(doc: Document) -> None:
section = doc.sections[0]
section.top_margin = Inches(1)
section.bottom_margin = Inches(1)
section.left_margin = Inches(1)
section.right_margin = Inches(1)
styles = doc.styles
normal = styles["Normal"]
normal.font.name = "Calibri"
normal.font.size = Pt(11)
normal.paragraph_format.space_after = Pt(6)
normal.paragraph_format.line_spacing = 1.10
title = styles["Title"]
title.font.name = "Calibri"
title.font.size = Pt(22)
title.font.bold = True
title.font.color.rgb = RGBColor(11, 37, 69)
title.paragraph_format.space_after = Pt(8)
for style_name, size, color in [
("Heading 1", 16, RGBColor(46, 116, 181)),
("Heading 2", 13, RGBColor(46, 116, 181)),
("Heading 3", 12, RGBColor(31, 77, 120)),
]:
style = styles[style_name]
style.font.name = "Calibri"
style.font.size = Pt(size)
style.font.bold = True
style.font.color.rgb = color
style.paragraph_format.space_before = Pt(10)
style.paragraph_format.space_after = Pt(5)
def add_table(doc: Document, rows: list[list[str]]) -> None:
if not rows:
return
table = doc.add_table(rows=len(rows), cols=len(rows[0]))
table.alignment = WD_TABLE_ALIGNMENT.CENTER
table.style = "Table Grid"
for r_idx, row in enumerate(rows):
for c_idx, value in enumerate(row):
cell = table.cell(r_idx, c_idx)
cell.text = value.strip()
cell.vertical_alignment = WD_CELL_VERTICAL_ALIGNMENT.CENTER
set_cell_margins(cell)
for paragraph in cell.paragraphs:
paragraph.paragraph_format.space_after = Pt(0)
for run in paragraph.runs:
run.font.size = Pt(9)
run.font.name = "Calibri"
if r_idx == 0:
run.font.bold = True
if r_idx == 0:
set_cell_shading(cell, "F2F4F7")
doc.add_paragraph()
def parse_table(lines: list[str], start: int) -> tuple[list[list[str]], int]:
rows = []
i = start
while i < len(lines) and lines[i].strip().startswith("|"):
raw = lines[i].strip()
parts = [part.strip() for part in raw.strip("|").split("|")]
if not all(re.fullmatch(r":?-{3,}:?", part or "") for part in parts):
rows.append(parts)
i += 1
return rows, i
def build() -> None:
doc = Document()
style_document(doc)
title = doc.add_paragraph(style="Title")
title.alignment = WD_ALIGN_PARAGRAPH.CENTER
title.add_run("Turkish Legal RAG: Base vs Fine-Tuned RAG Evaluation")
meta = doc.add_paragraph()
meta.alignment = WD_ALIGN_PARAGRAPH.CENTER
run = meta.add_run("CENG493 Term Project - Final Technical Report")
run.italic = True
run.font.color.rgb = RGBColor(85, 85, 85)
doc.add_paragraph()
lines = SOURCE.read_text(encoding="utf-8").splitlines()
in_code = False
code_buffer: list[str] = []
i = 0
while i < len(lines):
line = lines[i].rstrip()
stripped = line.strip()
if stripped.startswith("```"):
if in_code:
para = doc.add_paragraph()
para.style = doc.styles["No Spacing"]
code_run = para.add_run("\n".join(code_buffer))
code_run.font.name = "Consolas"
code_run.font.size = Pt(9)
code_buffer = []
in_code = False
else:
in_code = True
i += 1
continue
if in_code:
code_buffer.append(line)
i += 1
continue
if not stripped:
i += 1
continue
if stripped.startswith("|"):
rows, i = parse_table(lines, i)
add_table(doc, rows)
continue
if stripped.startswith("# "):
# The title is already rendered as a cover title.
i += 1
continue
if stripped.startswith("## "):
doc.add_paragraph(stripped[3:], style="Heading 1")
elif stripped.startswith("### "):
doc.add_paragraph(stripped[4:], style="Heading 2")
elif stripped.startswith("- "):
doc.add_paragraph(stripped[2:], style="List Bullet")
elif re.match(r"^\d+\. ", stripped):
doc.add_paragraph(re.sub(r"^\d+\. ", "", stripped), style="List Number")
else:
doc.add_paragraph(stripped)
i += 1
footer = doc.sections[0].footer.paragraphs[0]
footer.alignment = WD_ALIGN_PARAGRAPH.CENTER
footer.add_run("Turkish Legal RAG - CENG493").font.size = Pt(9)
OUTPUT.parent.mkdir(parents=True, exist_ok=True)
doc.save(OUTPUT)
print(f"Wrote {OUTPUT}")
if __name__ == "__main__":
build()