tomo2chin2 commited on
Commit
f107614
·
verified ·
1 Parent(s): 371916d

Add scale=0.9 to PDF generation for better A4 page fitting

Browse files
Files changed (1) hide show
  1. app.py +130 -0
app.py CHANGED
@@ -1,10 +1,13 @@
1
  import os
2
  import uuid
3
  import tempfile
 
 
4
  from datetime import datetime
5
  from pathlib import Path
6
  from typing import Optional
7
 
 
8
  from fastapi import FastAPI, HTTPException, File, UploadFile
9
  from fastapi.responses import JSONResponse, RedirectResponse
10
  from fastapi.middleware.cors import CORSMiddleware
@@ -17,6 +20,10 @@ class HTMLRequest(BaseModel):
17
  html_content: str
18
 
19
 
 
 
 
 
20
  class PDFResponse(BaseModel):
21
  pdf_url: str
22
  filename: str
@@ -24,6 +31,91 @@ class PDFResponse(BaseModel):
24
  repository_url: str
25
 
26
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
27
  async def html_to_pdf_api(html_content: str) -> tuple[str, str]:
28
  """
29
  HTMLコンテンツをPDFに変換してHugging Faceデータセットリポジトリにアップロード
@@ -128,6 +220,7 @@ async def root():
128
  "storage": f"Hugging Face Dataset Repository: {hf_repo_id}",
129
  "endpoints": {
130
  "convert": "/convert - HTMLをPDFに変換してHFリポジトリに保存",
 
131
  "files": "/files - HFリポジトリ内のPDFファイル一覧",
132
  "docs": "/docs - API仕様書",
133
  "health": "/health - ヘルスチェック"
@@ -192,6 +285,43 @@ async def health_check():
192
  return {"status": "healthy", "timestamp": datetime.now().isoformat()}
193
 
194
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
195
  @app.get("/files")
196
  async def list_files():
197
  """
 
1
  import os
2
  import uuid
3
  import tempfile
4
+ import json
5
+ import re
6
  from datetime import datetime
7
  from pathlib import Path
8
  from typing import Optional
9
 
10
+ import httpx
11
  from fastapi import FastAPI, HTTPException, File, UploadFile
12
  from fastapi.responses import JSONResponse, RedirectResponse
13
  from fastapi.middleware.cors import CORSMiddleware
 
20
  html_content: str
21
 
22
 
23
+ class YAMLRequest(BaseModel):
24
+ yaml_content: str
25
+
26
+
27
  class PDFResponse(BaseModel):
28
  pdf_url: str
29
  filename: str
 
31
  repository_url: str
32
 
33
 
34
+ async def call_gemini_api(yaml_content: str) -> str:
35
+ """
36
+ Gemini APIを呼び出してYAMLコンテンツからHTMLを生成
37
+ """
38
+ try:
39
+ # 環境変数から設定を取得
40
+ gemini_api_key = os.getenv("GEMINI_API_KEY")
41
+ model_id = os.getenv("MODEL", "gemini-2.5-flash")
42
+ system_instruction = os.getenv("SYSTEM", "YAMLデータを基にHTMLを生成してください。")
43
+
44
+ if not gemini_api_key:
45
+ raise HTTPException(status_code=500, detail="GEMINI_API_KEY環境変数が設定されていません")
46
+
47
+ # Gemini APIリクエストボディを構築
48
+ request_body = {
49
+ "contents": [
50
+ {
51
+ "role": "user",
52
+ "parts": [
53
+ {
54
+ "text": system_instruction
55
+ },
56
+ ]
57
+ },
58
+ {
59
+ "role": "model",
60
+ "parts": [
61
+ {
62
+ "text": "はい、システムインストラクションとして認識しました。\nYAMLデータを基にHTMLを生成いたします。"
63
+ },
64
+ ]
65
+ },
66
+ {
67
+ "role": "user",
68
+ "parts": [
69
+ {
70
+ "text": yaml_content
71
+ },
72
+ ]
73
+ },
74
+ ],
75
+ "generationConfig": {
76
+ "temperature": 0.75,
77
+ "responseMimeType": "text/plain",
78
+ },
79
+ }
80
+
81
+ # Gemini APIを呼び出し
82
+ url = f"https://generativelanguage.googleapis.com/v1beta/models/{model_id}:generateContent?key={gemini_api_key}"
83
+
84
+ async with httpx.AsyncClient(timeout=30.0) as client:
85
+ response = await client.post(url, json=request_body)
86
+ response.raise_for_status()
87
+
88
+ # レスポンスからHTMLを抽出
89
+ result = response.json()
90
+
91
+ # レスポンス構造を解析してHTMLを抽出
92
+ if "candidates" in result and len(result["candidates"]) > 0:
93
+ candidate = result["candidates"][0]
94
+ if "content" in candidate and "parts" in candidate["content"]:
95
+ parts = candidate["content"]["parts"]
96
+ for part in parts:
97
+ if "text" in part:
98
+ text = part["text"]
99
+ # HTMLタグを含む部分を抽出
100
+ html_match = re.search(r'<html.*?>.*?</html>', text, re.DOTALL | re.IGNORECASE)
101
+ if html_match:
102
+ return html_match.group(0)
103
+ # HTML全体がない場合はbodyタグを探す
104
+ body_match = re.search(r'<body.*?>.*?</body>', text, re.DOTALL | re.IGNORECASE)
105
+ if body_match:
106
+ return f"<html><head><meta charset='utf-8'></head>{body_match.group(0)}</html>"
107
+ # それでもない場合は全体をHTMLとして扱う
108
+ if "<" in text and ">" in text:
109
+ return text
110
+
111
+ raise HTTPException(status_code=500, detail="Gemini APIレスポンスからHTMLを抽出できませんでした")
112
+
113
+ except httpx.HTTPError as e:
114
+ raise HTTPException(status_code=500, detail=f"Gemini API呼び出しエラー: {str(e)}")
115
+ except Exception as e:
116
+ raise HTTPException(status_code=500, detail=f"Gemini API処理中にエラーが発生しました: {str(e)}")
117
+
118
+
119
  async def html_to_pdf_api(html_content: str) -> tuple[str, str]:
120
  """
121
  HTMLコンテンツをPDFに変換してHugging Faceデータセットリポジトリにアップロード
 
220
  "storage": f"Hugging Face Dataset Repository: {hf_repo_id}",
221
  "endpoints": {
222
  "convert": "/convert - HTMLをPDFに変換してHFリポジトリに保存",
223
+ "convert-yaml": "/convert-yaml - YAMLをGemini APIでHTMLに変換してPDF化",
224
  "files": "/files - HFリポジトリ内のPDFファイル一覧",
225
  "docs": "/docs - API仕様書",
226
  "health": "/health - ヘルスチェック"
 
285
  return {"status": "healthy", "timestamp": datetime.now().isoformat()}
286
 
287
 
288
+ @app.post("/convert-yaml", response_model=PDFResponse)
289
+ async def convert_yaml_to_pdf(request: YAMLRequest):
290
+ """
291
+ YAMLコンテンツをGemini APIでHTMLに変換し、PDFを生成してHugging Faceデータセットリポジトリに保存するエンドポイント
292
+
293
+ - **yaml_content**: 変換するYAMLコンテンツ
294
+
295
+ Returns:
296
+ - **pdf_url**: 生成されたPDFのダウンロードURL(Hugging Face上)
297
+ - **filename**: PDFファイル名
298
+ - **message**: 処理結果メッセージ
299
+ - **repository_url**: リポジトリURL
300
+ """
301
+ if not request.yaml_content or not request.yaml_content.strip():
302
+ raise HTTPException(status_code=400, detail="YAMLコンテンツが空です")
303
+
304
+ try:
305
+ # YAMLからGemini APIでHTMLを生成
306
+ html_content = await call_gemini_api(request.yaml_content)
307
+
308
+ # 生成されたHTMLをPDFに変換
309
+ filename, pdf_url = await html_to_pdf_api(html_content)
310
+ hf_repo_id = os.getenv("HF_DATASET_REPO_ID")
311
+ repository_url = f"https://huggingface.co/datasets/{hf_repo_id}"
312
+
313
+ return PDFResponse(
314
+ pdf_url=pdf_url,
315
+ filename=filename,
316
+ message=f"✅ YAMLからPDFが正常に生成され、Hugging Faceリポジトリに保存されました: {filename}",
317
+ repository_url=repository_url
318
+ )
319
+ except HTTPException:
320
+ raise
321
+ except Exception as e:
322
+ raise HTTPException(status_code=500, detail=f"処理中にエラーが発生しました: {str(e)}")
323
+
324
+
325
  @app.get("/files")
326
  async def list_files():
327
  """