Spaces:
Sleeping
Sleeping
Update app.py
Browse files
app.py
CHANGED
|
@@ -12,9 +12,8 @@ from openai import OpenAI
|
|
| 12 |
from reportlab.lib import colors
|
| 13 |
from reportlab.lib.pagesizes import A4
|
| 14 |
from reportlab.platypus import SimpleDocTemplate, Table, TableStyle, Paragraph, Spacer
|
| 15 |
-
from reportlab.lib.styles import getSampleStyleSheet
|
| 16 |
from reportlab.pdfbase import pdfmetrics
|
| 17 |
-
from reportlab.pdfbase.ttfonts import TTFont
|
| 18 |
from reportlab.pdfbase.cidfonts import UnicodeCIDFont
|
| 19 |
|
| 20 |
# --------------------
|
|
@@ -26,7 +25,6 @@ FALLBACK_TEXT = """
|
|
| 26 |
### 1. 可能相關疾病
|
| 27 |
- 胃食道逆流(GERD):胃酸回流到食道,可能引起燒灼感和胃痛。
|
| 28 |
- 胃潰瘍:胃內部的黏膜受損,形成潰瘍,可能導致疼痛和不適。
|
| 29 |
-
- 胃炎:胃黏膜的炎症,通常伴隨疼痛、噁心和消化不良。
|
| 30 |
### 2. 飲食營養建議
|
| 31 |
- 纖維素:有助於消化,減少便秘和腸道不適。
|
| 32 |
- 維生素C:幫助修復胃黏膜,增強免疫系統。
|
|
@@ -43,7 +41,7 @@ FALLBACK_TEXT = """
|
|
| 43 |
""".strip()
|
| 44 |
|
| 45 |
# --------------------
|
| 46 |
-
# GPT 呼叫
|
| 47 |
# --------------------
|
| 48 |
def call_gpt_system(prompt: str) -> str:
|
| 49 |
if client is None:
|
|
@@ -51,8 +49,9 @@ def call_gpt_system(prompt: str) -> str:
|
|
| 51 |
try:
|
| 52 |
resp = client.chat.completions.create(
|
| 53 |
model="gpt-4o-mini",
|
|
|
|
|
|
|
| 54 |
messages=[{"role": "user", "content": prompt}],
|
| 55 |
-
temperature=0.7,
|
| 56 |
)
|
| 57 |
text = resp.choices[0].message.content
|
| 58 |
return text if text and text.strip() else FALLBACK_TEXT
|
|
@@ -61,7 +60,7 @@ def call_gpt_system(prompt: str) -> str:
|
|
| 61 |
return FALLBACK_TEXT
|
| 62 |
|
| 63 |
# --------------------
|
| 64 |
-
# 解析
|
| 65 |
# --------------------
|
| 66 |
def extract_block(text: str, starts: list, ends: list = None) -> str:
|
| 67 |
if not text:
|
|
@@ -159,7 +158,7 @@ def robust_parse(text: str) -> Tuple[pd.DataFrame, pd.DataFrame, pd.DataFrame]:
|
|
| 159 |
return df_disease, df_nutrition, df_meals
|
| 160 |
|
| 161 |
# --------------------
|
| 162 |
-
# PDF 生成
|
| 163 |
# --------------------
|
| 164 |
def has_chinese(s: str) -> bool:
|
| 165 |
return bool(re.search(r"[\u4e00-\u9fff]", str(s)))
|
|
@@ -175,12 +174,8 @@ def generate_pdf_file(df_disease, df_nutrition, df_meals, basic_info=None):
|
|
| 175 |
df_nutrition = clean_dataframe(df_nutrition)
|
| 176 |
df_meals = clean_dataframe(df_meals)
|
| 177 |
|
| 178 |
-
|
| 179 |
-
|
| 180 |
-
pdfmetrics.registerFont(UnicodeCIDFont("STSong-Light"))
|
| 181 |
-
font_name = "STSong-Light"; font_ok = True
|
| 182 |
-
except:
|
| 183 |
-
pass
|
| 184 |
|
| 185 |
file_prefix = basic_info.get("name", "個人") if basic_info else "個人"
|
| 186 |
safe_name = re.sub(r"[^\w\u4e00-\u9fff]", "_", file_prefix)
|
|
@@ -192,9 +187,9 @@ def generate_pdf_file(df_disease, df_nutrition, df_meals, basic_info=None):
|
|
| 192 |
styles = getSampleStyleSheet()
|
| 193 |
title_style = styles["Title"].clone("title")
|
| 194 |
heading_style = styles["Heading2"].clone("h2")
|
| 195 |
-
normal_style =
|
| 196 |
-
|
| 197 |
-
|
| 198 |
|
| 199 |
# 標題
|
| 200 |
elements.append(Paragraph(f"健康分析與飲食建議報告 — {file_prefix}", title_style))
|
|
@@ -214,10 +209,8 @@ def generate_pdf_file(df_disease, df_nutrition, df_meals, basic_info=None):
|
|
| 214 |
style = TableStyle([
|
| 215 |
("GRID", (0, 0), (-1, -1), 0.8, colors.black),
|
| 216 |
("BACKGROUND", (0, 0), (-1, 0), colors.lightgrey),
|
| 217 |
-
("
|
| 218 |
])
|
| 219 |
-
if font_ok:
|
| 220 |
-
style.add("FONTNAME", (0, 0), (-1, -1), font_name)
|
| 221 |
t.setStyle(style)
|
| 222 |
elements.append(t)
|
| 223 |
elements.append(Spacer(1, 16))
|
|
@@ -225,45 +218,27 @@ def generate_pdf_file(df_disease, df_nutrition, df_meals, basic_info=None):
|
|
| 225 |
# 疾病表
|
| 226 |
if not df_disease.empty:
|
| 227 |
elements.append(Paragraph("一、可能相關疾病", heading_style))
|
| 228 |
-
table_data = [df_disease.columns.tolist()] + df_disease.values.tolist()
|
| 229 |
t = Table(table_data, colWidths=[150, 330])
|
| 230 |
-
|
| 231 |
-
("GRID", (0, 0), (-1, -1), 0.8, colors.black),
|
| 232 |
-
("BACKGROUND", (0, 0), (-1, 0), colors.lightgrey),
|
| 233 |
-
])
|
| 234 |
-
if font_ok:
|
| 235 |
-
style.add("FONTNAME", (0, 0), (-1, -1), font_name)
|
| 236 |
-
t.setStyle(style)
|
| 237 |
elements.append(t)
|
| 238 |
elements.append(Spacer(1, 12))
|
| 239 |
|
| 240 |
# 營養建議表
|
| 241 |
if not df_nutrition.empty:
|
| 242 |
elements.append(Paragraph("二、飲食營養建議", heading_style))
|
| 243 |
-
table_data = [df_nutrition.columns.tolist()] + df_nutrition.values.tolist()
|
| 244 |
t = Table(table_data, colWidths=[150, 330])
|
| 245 |
-
|
| 246 |
-
("GRID", (0, 0), (-1, -1), 0.8, colors.black),
|
| 247 |
-
("BACKGROUND", (0, 0), (-1, 0), colors.lightgrey),
|
| 248 |
-
])
|
| 249 |
-
if font_ok:
|
| 250 |
-
style.add("FONTNAME", (0, 0), (-1, -1), font_name)
|
| 251 |
-
t.setStyle(style)
|
| 252 |
elements.append(t)
|
| 253 |
elements.append(Spacer(1, 12))
|
| 254 |
|
| 255 |
-
# 餐點表
|
| 256 |
if not df_meals.empty:
|
| 257 |
elements.append(Paragraph("三、一日三餐(中式 / 西式)", heading_style))
|
| 258 |
-
table_data = [df_meals.columns.tolist()] + df_meals.values.tolist()
|
| 259 |
t = Table(table_data, colWidths=[240, 240])
|
| 260 |
-
|
| 261 |
-
("GRID", (0, 0), (-1, -1), 0.8, colors.black),
|
| 262 |
-
("BACKGROUND", (0, 0), (-1, 0), colors.lightgrey),
|
| 263 |
-
])
|
| 264 |
-
if font_ok:
|
| 265 |
-
style.add("FONTNAME", (0, 0), (-1, -1), font_name)
|
| 266 |
-
t.setStyle(style)
|
| 267 |
elements.append(t)
|
| 268 |
|
| 269 |
doc.build(elements)
|
|
@@ -300,7 +275,7 @@ css_code = """
|
|
| 300 |
"""
|
| 301 |
|
| 302 |
with gr.Blocks(css=css_code) as demo:
|
| 303 |
-
gr.Markdown("##
|
| 304 |
with gr.Row():
|
| 305 |
with gr.Column(scale=1):
|
| 306 |
gr.Markdown("### 請輸入基本資料")
|
|
|
|
| 12 |
from reportlab.lib import colors
|
| 13 |
from reportlab.lib.pagesizes import A4
|
| 14 |
from reportlab.platypus import SimpleDocTemplate, Table, TableStyle, Paragraph, Spacer
|
| 15 |
+
from reportlab.lib.styles import getSampleStyleSheet, ParagraphStyle
|
| 16 |
from reportlab.pdfbase import pdfmetrics
|
|
|
|
| 17 |
from reportlab.pdfbase.cidfonts import UnicodeCIDFont
|
| 18 |
|
| 19 |
# --------------------
|
|
|
|
| 25 |
### 1. 可能相關疾病
|
| 26 |
- 胃食道逆流(GERD):胃酸回流到食道,可能引起燒灼感和胃痛。
|
| 27 |
- 胃潰瘍:胃內部的黏膜受損,形成潰瘍,可能導致疼痛和不適。
|
|
|
|
| 28 |
### 2. 飲食營養建議
|
| 29 |
- 纖維素:有助於消化,減少便秘和腸道不適。
|
| 30 |
- 維生素C:幫助修復胃黏膜,增強免疫系統。
|
|
|
|
| 41 |
""".strip()
|
| 42 |
|
| 43 |
# --------------------
|
| 44 |
+
# GPT 呼叫(加速版)
|
| 45 |
# --------------------
|
| 46 |
def call_gpt_system(prompt: str) -> str:
|
| 47 |
if client is None:
|
|
|
|
| 49 |
try:
|
| 50 |
resp = client.chat.completions.create(
|
| 51 |
model="gpt-4o-mini",
|
| 52 |
+
max_tokens=500, # 限制字數以加快速度
|
| 53 |
+
temperature=0.6, # 適度隨機
|
| 54 |
messages=[{"role": "user", "content": prompt}],
|
|
|
|
| 55 |
)
|
| 56 |
text = resp.choices[0].message.content
|
| 57 |
return text if text and text.strip() else FALLBACK_TEXT
|
|
|
|
| 60 |
return FALLBACK_TEXT
|
| 61 |
|
| 62 |
# --------------------
|
| 63 |
+
# 區塊解析
|
| 64 |
# --------------------
|
| 65 |
def extract_block(text: str, starts: list, ends: list = None) -> str:
|
| 66 |
if not text:
|
|
|
|
| 158 |
return df_disease, df_nutrition, df_meals
|
| 159 |
|
| 160 |
# --------------------
|
| 161 |
+
# PDF 生成(修正版:文字換行)
|
| 162 |
# --------------------
|
| 163 |
def has_chinese(s: str) -> bool:
|
| 164 |
return bool(re.search(r"[\u4e00-\u9fff]", str(s)))
|
|
|
|
| 174 |
df_nutrition = clean_dataframe(df_nutrition)
|
| 175 |
df_meals = clean_dataframe(df_meals)
|
| 176 |
|
| 177 |
+
pdfmetrics.registerFont(UnicodeCIDFont("STSong-Light"))
|
| 178 |
+
font_name = "STSong-Light"
|
|
|
|
|
|
|
|
|
|
|
|
|
| 179 |
|
| 180 |
file_prefix = basic_info.get("name", "個人") if basic_info else "個人"
|
| 181 |
safe_name = re.sub(r"[^\w\u4e00-\u9fff]", "_", file_prefix)
|
|
|
|
| 187 |
styles = getSampleStyleSheet()
|
| 188 |
title_style = styles["Title"].clone("title")
|
| 189 |
heading_style = styles["Heading2"].clone("h2")
|
| 190 |
+
normal_style = ParagraphStyle("normal", fontName=font_name, fontSize=10, leading=14)
|
| 191 |
+
|
| 192 |
+
title_style.fontName = heading_style.fontName = font_name
|
| 193 |
|
| 194 |
# 標題
|
| 195 |
elements.append(Paragraph(f"健康分析與飲食建議報告 — {file_prefix}", title_style))
|
|
|
|
| 209 |
style = TableStyle([
|
| 210 |
("GRID", (0, 0), (-1, -1), 0.8, colors.black),
|
| 211 |
("BACKGROUND", (0, 0), (-1, 0), colors.lightgrey),
|
| 212 |
+
("FONTNAME", (0, 0), (-1, -1), font_name),
|
| 213 |
])
|
|
|
|
|
|
|
| 214 |
t.setStyle(style)
|
| 215 |
elements.append(t)
|
| 216 |
elements.append(Spacer(1, 16))
|
|
|
|
| 218 |
# 疾病表
|
| 219 |
if not df_disease.empty:
|
| 220 |
elements.append(Paragraph("一、可能相關疾病", heading_style))
|
| 221 |
+
table_data = [df_disease.columns.tolist()] + [[Paragraph(str(c), normal_style) for c in row] for row in df_disease.values.tolist()]
|
| 222 |
t = Table(table_data, colWidths=[150, 330])
|
| 223 |
+
t.setStyle(TableStyle([("GRID", (0, 0), (-1, -1), 0.8, colors.black), ("BACKGROUND", (0, 0), (-1, 0), colors.lightgrey), ("FONTNAME", (0, 0), (-1, -1), font_name)]))
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 224 |
elements.append(t)
|
| 225 |
elements.append(Spacer(1, 12))
|
| 226 |
|
| 227 |
# 營養建議表
|
| 228 |
if not df_nutrition.empty:
|
| 229 |
elements.append(Paragraph("二、飲食營養建議", heading_style))
|
| 230 |
+
table_data = [df_nutrition.columns.tolist()] + [[Paragraph(str(c), normal_style) for c in row] for row in df_nutrition.values.tolist()]
|
| 231 |
t = Table(table_data, colWidths=[150, 330])
|
| 232 |
+
t.setStyle(TableStyle([("GRID", (0, 0), (-1, -1), 0.8, colors.black), ("BACKGROUND", (0, 0), (-1, 0), colors.lightgrey), ("FONTNAME", (0, 0), (-1, -1), font_name)]))
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 233 |
elements.append(t)
|
| 234 |
elements.append(Spacer(1, 12))
|
| 235 |
|
| 236 |
+
# 餐點表(換行修正)
|
| 237 |
if not df_meals.empty:
|
| 238 |
elements.append(Paragraph("三、一日三餐(中式 / 西式)", heading_style))
|
| 239 |
+
table_data = [df_meals.columns.tolist()] + [[Paragraph(str(c), normal_style) for c in row] for row in df_meals.values.tolist()]
|
| 240 |
t = Table(table_data, colWidths=[240, 240])
|
| 241 |
+
t.setStyle(TableStyle([("GRID", (0, 0), (-1, -1), 0.8, colors.black), ("BACKGROUND", (0, 0), (-1, 0), colors.lightgrey), ("FONTNAME", (0, 0), (-1, -1), font_name)]))
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 242 |
elements.append(t)
|
| 243 |
|
| 244 |
doc.build(elements)
|
|
|
|
| 275 |
"""
|
| 276 |
|
| 277 |
with gr.Blocks(css=css_code) as demo:
|
| 278 |
+
gr.Markdown("## 🩺AI食療專家")
|
| 279 |
with gr.Row():
|
| 280 |
with gr.Column(scale=1):
|
| 281 |
gr.Markdown("### 請輸入基本資料")
|