Spaces:
Running
Running
| 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() | |