Corin1998 commited on
Commit
ec337b7
·
verified ·
1 Parent(s): efc6dd5

Update irpr/main.py

Browse files
Files changed (1) hide show
  1. irpr/main.py +59 -67
irpr/main.py CHANGED
@@ -1,30 +1,25 @@
1
  from fastapi import FastAPI, HTTPException, Response, UploadFile, File, Request
2
- from fastapi.responses import HTMLResponse
3
  from fastapi.staticfiles import StaticFiles
4
  from fastapi.middleware.cors import CORSMiddleware
5
  from fastapi.templating import Jinja2Templates
 
 
6
  from irpr.models import IngestRequest, GenerateRequest
7
- import os
8
 
9
- app = FastAPI(title="IR/PR Co-Pilot Pro", version="0.2.0")
10
 
11
- # --- CORS(同一オリジン運用想定だが念のため全許可) ---
12
  app.add_middleware(
13
- CORSMiddleware,
14
- allow_origins=["*"], allow_credentials=True, allow_methods=["*"], allow_headers=["*"],
15
  )
16
 
17
- # --- 静的 & 生成物の公開マウント ---
18
- os.makedirs("static", exist_ok=True)
19
- os.makedirs("templates", exist_ok=True)
20
- os.makedirs("data", exist_ok=True)
21
- os.makedirs("data/uploads", exist_ok=True)
22
-
23
  app.mount("/static", StaticFiles(directory="static"), name="static")
24
  app.mount("/files", StaticFiles(directory="data"), name="files")
25
  templates = Jinja2Templates(directory="templates")
26
 
27
- # ---------------- UI ----------------
28
  @app.get("/", response_class=HTMLResponse)
29
  def ui(request: Request):
30
  return templates.TemplateResponse("index.html", {"request": request})
@@ -33,73 +28,70 @@ def ui(request: Request):
33
  def health():
34
  return {"ok": True, "service": "IR/PR Co-Pilot Pro"}
35
 
36
- # ------------- API: 取込 -------------
37
  @app.post("/ingest/edinet")
38
  def ingest_edinet(req: IngestRequest):
39
  from rag.ingest import ingest_edinet_for_company
40
- n = ingest_edinet_for_company(req.edinet_code, req.date)
41
- return {"ingested_chunks": n}
 
 
 
42
 
43
  @app.post("/ingest/upload")
44
  async def ingest_upload(files: list[UploadFile] = File(...)):
45
  from rag.ingest import ingest_pdf_bytes
46
- total = 0
47
- saved = []
48
- for f in files:
49
- if not f.filename.lower().endswith(".pdf"):
50
- continue
51
- blob = await f.read()
52
- # 保存してダウンロード可能に
53
- save_path = os.path.join("data", "uploads", f.filename)
54
- with open(save_path, "wb") as w:
55
- w.write(blob)
56
- source_url = f"/files/uploads/{f.filename}"
57
- total += ingest_pdf_bytes(title=f.filename, source_url=source_url, pdf_bytes=blob)
58
- saved.append(source_url)
59
- return {"ingested_chunks": total, "saved": saved}
60
 
61
- # ------------- API: 生成 -------------
62
  @app.post("/generate/all")
63
  def generate_all(req: GenerateRequest):
64
- from generators.summary import make_summary
65
- from generators.qa import make_qa
66
- from export.ppt import build_deck, save_pptx
67
- from export.qa_csv import save_qa_csv
68
-
69
- summary_text, links = make_summary(req.query)
70
- sections = {
71
- "highlights": _extract_section(summary_text, "業績ハイライト"),
72
- "outlook": _extract_section(summary_text, "見通し"),
73
- "segments": _extract_section(summary_text, "セグメント"),
74
- "finance": _extract_section(summary_text, "財務"),
75
- "shareholder":_extract_section(summary_text, "株主還元"),
76
- "esg": _extract_section(summary_text, "ESG"),
77
- "risks": _extract_section(summary_text, "リスク"),
78
- }
79
- qa_list, links2 = make_qa(req.query, 30)
80
- prs = build_deck(sections, links)
81
- ppt_path = save_pptx(prs, "data/ir_summary.pptx")
82
- csv_path = save_qa_csv(qa_list, links2, "data/qa_30.csv")
83
- return {
84
- "pptx": ppt_path.replace("data/", "/files/"),
85
- "qa_csv": csv_path.replace("data/", "/files/"),
86
- "links": links
87
- }
88
 
89
- # ------------- API: EDINET PDF 直配 -------------
90
- @app.get("/proxy/edinet/{doc_id}")
91
- def proxy_edinet(doc_id: str, type: str = "pdf"):
92
- if type != "pdf":
93
- raise HTTPException(400, "unsupported type")
94
- from rag.ingest import download_edinet_pdf
95
- pdf = download_edinet_pdf(doc_id)
96
- if not pdf:
97
- raise HTTPException(404, "file not found")
98
- return Response(content=pdf, media_type="application/pdf")
 
 
 
 
 
 
 
 
 
 
 
 
 
 
99
 
100
- # ------------- helper -------------
101
  def _extract_section(text: str, head: str):
102
  import re
103
- pat = rf"{head}[::]\s*(.*?)(?:\n-\s*\S+[::]|\Z)"
 
104
  m = re.search(pat, text, re.S)
105
  return (m.group(1).strip() if m else "").strip()
 
1
  from fastapi import FastAPI, HTTPException, Response, UploadFile, File, Request
2
+ from fastapi.responses import HTMLResponse, JSONResponse
3
  from fastapi.staticfiles import StaticFiles
4
  from fastapi.middleware.cors import CORSMiddleware
5
  from fastapi.templating import Jinja2Templates
6
+ import os, traceback
7
+
8
  from irpr.models import IngestRequest, GenerateRequest
 
9
 
10
+ app = FastAPI(title="IR/PR Co-Pilot Pro", version="0.3.1")
11
 
 
12
  app.add_middleware(
13
+ CORSMiddleware, allow_origins=["*"], allow_credentials=True,
14
+ allow_methods=["*"], allow_headers=["*"],
15
  )
16
 
17
+ for d in ["static", "templates", "data", "data/uploads"]:
18
+ os.makedirs(d, exist_ok=True)
 
 
 
 
19
  app.mount("/static", StaticFiles(directory="static"), name="static")
20
  app.mount("/files", StaticFiles(directory="data"), name="files")
21
  templates = Jinja2Templates(directory="templates")
22
 
 
23
  @app.get("/", response_class=HTMLResponse)
24
  def ui(request: Request):
25
  return templates.TemplateResponse("index.html", {"request": request})
 
28
  def health():
29
  return {"ok": True, "service": "IR/PR Co-Pilot Pro"}
30
 
31
+ # ---- ingest ----
32
  @app.post("/ingest/edinet")
33
  def ingest_edinet(req: IngestRequest):
34
  from rag.ingest import ingest_edinet_for_company
35
+ try:
36
+ n = ingest_edinet_for_company(req.edinet_code, req.date)
37
+ return {"ok": True, "ingested_chunks": n}
38
+ except Exception as e:
39
+ return {"ok": False, "error": repr(e)}
40
 
41
  @app.post("/ingest/upload")
42
  async def ingest_upload(files: list[UploadFile] = File(...)):
43
  from rag.ingest import ingest_pdf_bytes
44
+ total = 0; saved = []
45
+ try:
46
+ for f in files:
47
+ if not f.filename.lower().endswith(".pdf"): continue
48
+ blob = await f.read()
49
+ save_path = os.path.join("data", "uploads", f.filename)
50
+ with open(save_path, "wb") as w: w.write(blob)
51
+ source_url = f"/files/uploads/{f.filename}"
52
+ total += ingest_pdf_bytes(title=f.filename, source_url=source_url, pdf_bytes=blob)
53
+ saved.append(source_url)
54
+ return {"ok": True, "ingested_chunks": total, "saved": saved}
55
+ except Exception as e:
56
+ return {"ok": False, "error": repr(e), "ingested_chunks": total, "saved": saved}
 
57
 
58
+ # ---- generate ----
59
  @app.post("/generate/all")
60
  def generate_all(req: GenerateRequest):
61
+ try:
62
+ from generators.summary import make_summary
63
+ from generators.qa import make_qa
64
+ from export.ppt import build_deck, save_pptx
65
+ from export.qa_csv import save_qa_csv
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
66
 
67
+ summary_text, links = make_summary(req.query)
68
+ sections = {
69
+ "highlights": _extract_section(summary_text, "業績ハイライト"),
70
+ "outlook": _extract_section(summary_text, "見通し"),
71
+ "segments": _extract_section(summary_text, "セグメント"),
72
+ "finance": _extract_section(summary_text, "財務"),
73
+ "shareholder":_extract_section(summary_text, "株主還元"),
74
+ "esg": _extract_section(summary_text, "ESG"),
75
+ "risks": _extract_section(summary_text, "リスク"),
76
+ }
77
+ qa_list, links2 = make_qa(req.query, 30)
78
+ prs = build_deck(sections, links)
79
+ ppt_path = save_pptx(prs, "data/ir_summary.pptx")
80
+ csv_path = save_qa_csv(qa_list, links2, "data/qa_30.csv")
81
+ return {
82
+ "ok": True,
83
+ "pptx": ppt_path.replace("data/", "/files/"),
84
+ "qa_csv": csv_path.replace("data/", "/files/"),
85
+ "links": list(dict.fromkeys((links or []) + (links2 or [])))
86
+ }
87
+ except Exception as e:
88
+ # 500にせず詳細を返す(UIで表示)
89
+ traceback.print_exc()
90
+ return {"ok": False, "error": repr(e)}
91
 
 
92
  def _extract_section(text: str, head: str):
93
  import re
94
+ # 「見出し: 本文」形式を抽出。無ければ空。
95
+ pat = rf"{head}[::]\s*(.*?)(?:\n[^\n]*[::]|\Z)"
96
  m = re.search(pat, text, re.S)
97
  return (m.group(1).strip() if m else "").strip()