PersonaMate / app.py
kimddol's picture
Upload folder using huggingface_hub
c91f7f1 verified
import os, json, requests
import gradio as gr
from dotenv import load_dotenv
import httpx
import time
# ํ™˜๊ฒฝ ๋ณ€์ˆ˜ ๋กœ๋“œ
load_dotenv(os.path.join(os.path.dirname(__file__), "..", "backend", ".env"), override=True)
# Vercel ๋ฐฑ์—”๋“œ URL
BACKEND = os.getenv('BACKEND_URL', 'https://personamate-kimddols-projects.vercel.app')
async def fetch_data_fn():
try:
async with httpx.AsyncClient() as client:
res = await client.get(f"{BACKEND}/fetch_data", timeout=60)
res.raise_for_status()
return res.json()
except Exception as e:
return {"error": str(e)}
async def run_recommendations(yt, sns, mbti):
try:
payload = {
"youtube_subscriptions": [s.strip() for s in yt.splitlines() if s.strip()],
"sns_keywords": [s.strip() for s in sns.splitlines() if s.strip()],
"mbti": mbti
}
async with httpx.AsyncClient() as client:
res = await client.post(f"{BACKEND}/youtube/recommendations", json=payload, timeout=120)
res.raise_for_status()
data = res.json()
except Exception as e:
return "<h3>์ถ”์ฒœ ๊ฒฐ๊ณผ๋ฅผ ๊ฐ€์ ธ์˜ค๋Š” ๋ฐ ์‹คํŒจํ–ˆ์Šต๋‹ˆ๋‹ค.</h3>", f"API ํ˜ธ์ถœ ์‹คํŒจ: {e}", None
recommendations_data = data.get("recommendations", {})
youtube_recs = recommendations_data.get("youtube", [])
summary_reason = recommendations_data.get("summary_reason", "์ถ”์ฒœ ์‚ฌ์œ ๋ฅผ ์ƒ์„ฑํ•˜์ง€ ๋ชปํ–ˆ์Šต๋‹ˆ๋‹ค.")
if not youtube_recs:
return "<h3>์ถ”์ฒœ ๊ฒฐ๊ณผ๊ฐ€ ์—†์Šต๋‹ˆ๋‹ค.</h3>", summary_reason, None
table_html = "<table><thead><tr><th>์ฑ„๋„ ์ด๋ฆ„</th><th>์‚ฌ์ดํŠธ ์ฃผ์†Œ</th></tr></thead><tbody>"
for c in youtube_recs:
url = c.get("url", "")
name = c.get("name", "")
table_html += f'<tr><td>{name}</td><td><a href="{url}" target="_blank">{url}</a></td></tr>'
table_html += "</tbody></table>"
# Store the necessary data for export/email
state_data = {
"recommendations": {"youtube": youtube_recs},
"summary_reason": summary_reason
}
return table_html, summary_reason, state_data
async def export_file(file_type, recommendations_state):
if not recommendations_state:
return None, "๋จผ์ € ์ถ”์ฒœ์„ ์‹คํ–‰ํ•ด์ฃผ์„ธ์š”."
endpoint = f"{BACKEND}/youtube/recommendations/export/{file_type}"
try:
# The payload is already in the correct format in recommendations_state
async with httpx.AsyncClient() as client:
res = await client.post(endpoint, json=recommendations_state, timeout=60)
res.raise_for_status()
file_path = f"/tmp/recommendations_{int(time.time())}.{file_type}"
with open(file_path, "wb") as f:
f.write(res.content)
return file_path, f"{file_type.upper()} ํŒŒ์ผ ์ƒ์„ฑ ์™„๋ฃŒ"
except Exception as e:
return None, f"ํŒŒ์ผ ์ƒ์„ฑ ์‹คํŒจ: {e}"
async def send_email_fn(recipient_email, recommendations_state):
if not recommendations_state:
return "๋จผ์ € ์ถ”์ฒœ์„ ์‹คํ–‰ํ•ด์ฃผ์„ธ์š”."
if not recipient_email:
return "์ด๋ฉ”์ผ ์ฃผ์†Œ๋ฅผ ์ž…๋ ฅํ•ด์ฃผ์„ธ์š”."
endpoint = f"{BACKEND}/youtube/recommendations/email"
payload = {
"recipient_email": recipient_email,
"recommendations": recommendations_state.get("recommendations", {}),
"summary_reason": recommendations_state.get("summary_reason", "")
}
try:
async with httpx.AsyncClient() as client:
res = await client.post(endpoint, json=payload, timeout=60)
res.raise_for_status()
return "์ด๋ฉ”์ผ ์ „์†ก ์„ฑ๊ณต!"
except Exception as e:
return f"์ด๋ฉ”์ผ ์ „์†ก ์‹คํŒจ: {e}"
with gr.Blocks(title='PersonaMate Pro (OAuth + Simplified UI)') as demo:
recommendations_state = gr.State()
gr.Markdown('## PersonaMate Pro โ€” OAuth ์ˆ˜์ง‘ + ์ถ”์ฒœ UI')
with gr.Row():
with gr.Column(scale=1):
gr.Markdown('### 1) OAuth ๋กœ๊ทธ์ธ')
gr.HTML(f'<a href="{BACKEND}/oauth/google/start" target="_blank">Google (YouTube) ๋กœ๊ทธ์ธ</a>')
with gr.Column(scale=2):
gr.Markdown('### 2) ์ž๋™ ์ˆ˜์ง‘')
fetch_btn = gr.Button('๋‚ด ๊ณ„์ •์—์„œ ๋ฐ์ดํ„ฐ ์ˆ˜์ง‘')
fetch_result = gr.JSON(label="์ˆ˜์ง‘๋œ ๋ฐ์ดํ„ฐ ๋ฏธ๋ฆฌ๋ณด๊ธฐ")
with gr.Row():
with gr.Column(scale=1):
gr.Markdown('### 3) ์ž…๋ ฅ/MBTI')
yt_text = gr.Textbox(lines=6, label='์œ ํŠœ๋ธŒ ๊ตฌ๋… (์ˆ˜์ง‘/์ˆ˜๋™)')
sns_text = gr.Textbox(lines=6, label='SNS ํ‚ค์›Œ๋“œ/๊ณ„์ •')
mbti = gr.Dropdown(
choices=['ISTJ','ISFJ','INFJ','INTJ','ISTP','ISFP','INFP','INTP','ESTP','ESFP','ENFP','ENTP','ESTJ','ESFJ','ENFJ','ENTJ'],
value='ENFP',
label='MBTI'
)
run_btn = gr.Button('๋ถ„์„ & ์ถ”์ฒœ ์‹คํ–‰', variant='primary')
with gr.Column(scale=3):
gr.Markdown('### 4) ์ถ”์ฒœ ๊ฒฐ๊ณผ')
result_html = gr.HTML(label="์ถ”์ฒœ ๊ฒฐ๊ณผ")
gr.Markdown('### 5) ์ถ”์ฒœ ์‚ฌ์œ ')
summary_output = gr.Markdown(label="์ถ”์ฒœ ์‚ฌ์œ  ์š”์•ฝ")
with gr.Row():
gr.Markdown('### 6) ๊ฒฐ๊ณผ ์ €์žฅ ๋ฐ ๊ณต์œ ')
with gr.Row():
with gr.Column(scale=1):
html_btn = gr.Button("HTML ์ €์žฅ")
pdf_btn = gr.Button("PDF ์ €์žฅ")
download_file = gr.File(label="๋‹ค์šด๋กœ๋“œ")
with gr.Column(scale=2):
email_input = gr.Textbox(label="์ด๋ฉ”์ผ ์ฃผ์†Œ", placeholder="๊ฒฐ๊ณผ๋ฅผ ๋ฐ›์„ ์ด๋ฉ”์ผ์„ ์ž…๋ ฅํ•˜์„ธ์š”...")
email_btn = gr.Button("์ด๋ฉ”์ผ๋กœ ๋ณด๋‚ด๊ธฐ")
status_output = gr.Textbox(label="์ƒํƒœ", interactive=False)
fetch_btn.click(fetch_data_fn, inputs=[], outputs=[fetch_result])
run_btn.click(run_recommendations, [yt_text, sns_text, mbti], [result_html, summary_output, recommendations_state])
html_btn.click(export_file, inputs=[gr.State("html"), recommendations_state], outputs=[download_file, status_output])
pdf_btn.click(export_file, inputs=[gr.State("pdf"), recommendations_state], outputs=[download_file, status_output])
email_btn.click(send_email_fn, inputs=[email_input, recommendations_state], outputs=[status_output])
if __name__ == '__main__':
demo.launch()