7a31be9de5
- Configurable summary prompts via ENV or file; stronger default prompt. - New docx_styles.py: converts markdown (headings, bullets, bold/italic) to DOCX. - Updated create_summary_docx to use markdown-aware styling. - New docx_cover.py: reusable cover page for transcript and summary. - Cover pages enabled when COVER_PAGE_ENABLED=true.
119 lines
3.8 KiB
Python
119 lines
3.8 KiB
Python
"""
|
|
Reusable cover-page generator for transcript and summary DOCX files.
|
|
|
|
Configuration (env):
|
|
- COVER_PAGE_ENABLED: "true"/"false" (default: false)
|
|
- COVER_PAGE_ORGANIZATION: e.g., "A.P.Strom"
|
|
- COVER_PAGE_TITLE_PREFIX: e.g., "TRANSCRIPT" or "SUMMARY"
|
|
- COVER_PAGE_LOGO_URL: optional URL
|
|
- COVER_PAGE_LOGO_PATH: optional local path
|
|
"""
|
|
|
|
import os
|
|
from typing import Optional
|
|
from docx import Document
|
|
from docx.shared import Pt, Inches
|
|
from docx.enum.text import WD_ALIGN_PARAGRAPH
|
|
from docx.oxml import OxmlElement
|
|
from docx.oxml.ns import qn
|
|
|
|
|
|
def _add_page_break(doc: Document):
|
|
"""Insert a page break paragraph."""
|
|
p = doc.add_paragraph()
|
|
pPr = p._p.get_or_add_pPr()
|
|
# Clear spacing/tabs
|
|
for child in list(pPr):
|
|
tag = child.tag.split("}")[-1] if "}" in child.tag else child.tag
|
|
if tag in ("tabs", "spacing", "ind"):
|
|
pPr.remove(child)
|
|
page_break = OxmlElement("w:pageBreak")
|
|
page_break.set("{http://schemas.openxmlformats.org/wordprocessingml/2006/main}val", "1")
|
|
pPr.append(page_break)
|
|
|
|
|
|
def add_cover_page(
|
|
doc: Document,
|
|
title: str,
|
|
subtitle: Optional[str] = None,
|
|
metadata: Optional[dict] = None,
|
|
include_logo: bool = False,
|
|
):
|
|
"""
|
|
Insert a cover page at the current cursor position.
|
|
|
|
- title: e.g., "TRANSCRIPT" or "SUMMARY"
|
|
- subtitle: e.g., "Meeting of 16 June 2026"
|
|
- metadata: optional dict with keys like:
|
|
- "Organization"
|
|
- "Date"
|
|
- "Prepared by"
|
|
- "Reference"
|
|
"""
|
|
|
|
org = (os.getenv("COVER_PAGE_ORGANIZATION") or "").strip() or metadata.get("Organization") if metadata else None
|
|
date = (metadata.get("Date") if metadata else None) or ""
|
|
prepared_by = (metadata.get("Prepared by") if metadata else None) or ""
|
|
reference = (metadata.get("Reference") if metadata else None) or ""
|
|
|
|
# Title
|
|
p = doc.add_paragraph()
|
|
p.alignment = WD_ALIGN_PARAGRAPH.CENTER
|
|
p.paragraph_format.space_after = Pt(6)
|
|
run = p.add_run(title.upper())
|
|
run.bold = True
|
|
run.font.name = "Courier"
|
|
run.font.size = Pt(18)
|
|
|
|
# Subtitle
|
|
if subtitle:
|
|
p = doc.add_paragraph()
|
|
p.alignment = WD_ALIGN_PARAGRAPH.CENTER
|
|
p.paragraph_format.space_after = Pt(12)
|
|
run = p.add_run(subtitle)
|
|
run.font.name = "Courier"
|
|
run.font.size = Pt(14)
|
|
|
|
# Optional logo placeholder (text-only for now; can be extended)
|
|
if include_logo:
|
|
logo_url = (os.getenv("COVER_PAGE_LOGO_URL") or "").strip()
|
|
logo_path = (os.getenv("COVER_PAGE_LOGO_PATH") or "").strip()
|
|
# For now, just reserve space; image insertion can be added later.
|
|
p = doc.add_paragraph()
|
|
p.alignment = WD_ALIGN_PARAGRAPH.CENTER
|
|
p.paragraph_format.space_after = Pt(12)
|
|
|
|
# Metadata lines
|
|
if org or date or prepared_by or reference:
|
|
p = doc.add_paragraph()
|
|
p.alignment = WD_ALIGN_PARAGRAPH.CENTER
|
|
p.paragraph_format.space_after = Pt(4)
|
|
if org:
|
|
r = p.add_run(org)
|
|
r.font.name = "Courier"
|
|
r.font.size = Pt(12)
|
|
if date:
|
|
if org:
|
|
p.add_run("\n")
|
|
r = p.add_run(date)
|
|
r.font.name = "Courier"
|
|
r.font.size = Pt(12)
|
|
|
|
if prepared_by or reference:
|
|
p = doc.add_paragraph()
|
|
p.alignment = WD_ALIGN_PARAGRAPH.CENTER
|
|
p.paragraph_format.space_after = Pt(4)
|
|
if prepared_by:
|
|
r = p.add_run(f"Prepared by: {prepared_by}")
|
|
r.font.name = "Courier"
|
|
r.font.size = Pt(11)
|
|
if reference:
|
|
if prepared_by:
|
|
p.add_run("\n")
|
|
r = p.add_run(f"Reference: {reference}")
|
|
r.font.name = "Courier"
|
|
r.font.size = Pt(11)
|
|
|
|
# Page break after cover page
|
|
_add_page_break(doc)
|