Spaces:
Sleeping
Sleeping
File size: 4,433 Bytes
efc6dd5 a5ec4d8 efc6dd5 a5ec4d8 c204318 a5ec4d8 c204318 a5ec4d8 c204318 a5ec4d8 efc6dd5 c204318 |
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 |
# export/ppt.py
from __future__ import annotations
from typing import Dict, List, Tuple
from datetime import datetime
try:
from pptx import Presentation
from pptx.util import Inches, Pt
from pptx.enum.text import PP_ALIGN
from pptx.dml.color import RGBColor
except ModuleNotFoundError as e:
# 呼び出し側でこのエラーをユーザに分かりやすく返すため、敢えてそのまま投げる
raise
MAX_BULLETS = 10
def _add_title(prs: Presentation, title: str, subtitle: str | None = None):
slide_layout = prs.slide_layouts[0] # Title Slide
slide = prs.slides.add_slide(slide_layout)
slide.shapes.title.text = title
if subtitle is not None:
slide.placeholders[1].text = subtitle
def _add_title_and_bullets(prs: Presentation, title: str, bullets: List[str]):
slide_layout = prs.slide_layouts[1] # Title and Content
slide = prs.slides.add_slide(slide_layout)
slide.shapes.title.text = title
tx = slide.shapes.placeholders[1].text_frame
tx.clear()
# 先頭段落
if not bullets:
bullets = ["(該当なし)"]
bullets = bullets[:MAX_BULLETS]
p = tx.paragraphs[0]
p.text = bullets[0]
p.level = 0
for b in bullets[1:]:
rp = tx.add_paragraph()
rp.text = b
rp.level = 0
def _add_sources(prs: Presentation, links: List[str]):
slide_layout = prs.slide_layouts[5] # Title Only
slide = prs.slides.add_slide(slide_layout)
slide.shapes.title.text = "根拠リンク / 参考資料"
left = Inches(0.8); top = Inches(1.8); width = Inches(9); height = Inches(5)
box = slide.shapes.add_textbox(left, top, width, height)
tf = box.text_frame
tf.word_wrap = True
if not links:
tf.text = "(なし)"
return
tf.clear()
for i, url in enumerate(links, 1):
p = tf.add_paragraph() if i > 1 else tf.paragraphs[0]
p.text = f"{i}. {url}"
p.level = 0
def _split_lines(s: str, sep: str = "\n") -> List[str]:
s = (s or "").strip()
if not s:
return []
out = [ln.strip(" \t\u3000") for ln in s.split(sep)]
return [ln for ln in out if ln]
def build_deck(sections: Dict[str, str | List[str]], links: List[str] | None = None) -> Presentation:
"""
sections 例:
{
"highlights": "• 売上+10%\n• 営業益+5%\n...",
"outlook": "...",
"segments": "...",
"finance": "...",
"shareholder": "...",
"esg": "...",
"risks": "..."
}
"""
prs = Presentation()
# 1) タイトル
today = datetime.now().strftime("%Y-%m-%d")
_add_title(prs, "IR/PR Co-Pilot Pro 自動生成スライド", f"作成日: {today}")
# 2) 目次
toc = [
"業績ハイライト",
"見通し",
"セグメント",
"財務",
"株主還元",
"ESG",
"リスク",
"想定Q&A(抜粋)",
"参考リンク",
]
_add_title_and_bullets(prs, "目次", toc)
# 3) 各セクション
def bullets_of(key: str) -> List[str]:
v = sections.get(key, "") if sections else ""
if isinstance(v, list):
return [str(x) for x in v if str(x).strip()]
return _split_lines(str(v or ""))
_add_title_and_bullets(prs, "業績ハイライト", bullets_of("highlights"))
_add_title_and_bullets(prs, "見通し", bullets_of("outlook"))
_add_title_and_bullets(prs, "セグメント", bullets_of("segments"))
_add_title_and_bullets(prs, "財務", bullets_of("finance"))
_add_title_and_bullets(prs, "株主還元", bullets_of("shareholder"))
_add_title_and_bullets(prs, "ESG", bullets_of("esg"))
_add_title_and_bullets(prs, "リスク", bullets_of("risks"))
# 4) 想定Q&A(別モジュール側で上位N件だけ詰めて来てもOK)
qas = sections.get("qa_top", [])
if isinstance(qas, str):
qas = _split_lines(qas)
qa_bullets = []
if qas:
# "Q: ... / A: ..." 形式を1行ずつ箇条書き
for qa in qas[:MAX_BULLETS]:
qa_bullets.append(qa)
_add_title_and_bullets(prs, "想定Q&A(抜粋)", qa_bullets or ["(別添CSV参照)"])
# 5) 参考リンク
_add_sources(prs, list(dict.fromkeys(links or [])))
return prs
def save_pptx(prs: Presentation, path: str) -> None:
import os
os.makedirs(os.path.dirname(path), exist_ok=True)
prs.save(path)
|