ge3gsdgas / app.py
ssboost's picture
Update app.py
5953fb0 verified
import gradio as gr
import os
from gradio_client import Client
# ํ™˜๊ฒฝ๋ณ€์ˆ˜์—์„œ API ์—”๋“œํฌ์ธํŠธ ๋กœ๋“œ (๋กœ๊ทธ์— ์ถœ๋ ฅํ•˜์ง€ ์•Š์Œ)
def get_api_endpoint():
"""ํ™˜๊ฒฝ๋ณ€์ˆ˜์—์„œ API ์—”๋“œํฌ์ธํŠธ๋ฅผ ๊ฐ€์ ธ์˜ต๋‹ˆ๋‹ค."""
endpoint = os.getenv("API_ENDPOINT")
if not endpoint:
raise ValueError("API_ENDPOINT ํ™˜๊ฒฝ๋ณ€์ˆ˜๊ฐ€ ์„ค์ •๋˜์ง€ ์•Š์•˜์Šต๋‹ˆ๋‹ค.")
return endpoint
# ํด๋ผ์ด์–ธํŠธ ์ดˆ๊ธฐํ™” (๋กœ๊ทธ์— ์ •๋ณด ๋…ธ์ถœํ•˜์ง€ ์•Š์Œ)
try:
client = Client(get_api_endpoint())
except Exception as e:
print("ํด๋ผ์ด์–ธํŠธ ์ดˆ๊ธฐํ™” ์‹คํŒจ. ํ™˜๊ฒฝ๋ณ€์ˆ˜๋ฅผ ํ™•์ธํ•˜์„ธ์š”.")
client = None
def debug_log(message: str):
"""๋””๋ฒ„๊น… ๋กœ๊ทธ (์—”๋“œํฌ์ธํŠธ ์ •๋ณด๋Š” ์ œ์™ธ)"""
print(f"[DEBUG] {message}")
# --- ๋„ค์ด๋ฒ„ ๋ธ”๋กœ๊ทธ ์Šคํฌ๋ž˜ํ•‘ ํ•จ์ˆ˜ ---
def scrape_naver_blog(url: str) -> str:
"""๋„ค์ด๋ฒ„ ๋ธ”๋กœ๊ทธ ์Šคํฌ๋ž˜ํ•‘ - ํด๋ผ์ด์–ธํŠธ API ํ˜ธ์ถœ"""
debug_log("๋ธ”๋กœ๊ทธ ์Šคํฌ๋ž˜ํ•‘ ํ•จ์ˆ˜ ์‹œ์ž‘")
if not client:
return "ํด๋ผ์ด์–ธํŠธ๊ฐ€ ์ดˆ๊ธฐํ™”๋˜์ง€ ์•Š์•˜์Šต๋‹ˆ๋‹ค."
try:
result = client.predict(url, api_name="/fetch_blog_content")
debug_log("๋ธ”๋กœ๊ทธ ์Šคํฌ๋ž˜ํ•‘ ์™„๋ฃŒ")
return result
except Exception as e:
debug_log(f"๋ธ”๋กœ๊ทธ ์Šคํฌ๋ž˜ํ•‘ ์˜ค๋ฅ˜: {str(e)}")
return f"์Šคํฌ๋ž˜ํ•‘ ์ค‘ ์˜ค๋ฅ˜๊ฐ€ ๋ฐœ์ƒํ–ˆ์Šต๋‹ˆ๋‹ค: {str(e)}"
# --- ๋ถ„์„ ํ•ธ๋“ค๋Ÿฌ ํ•จ์ˆ˜ ---
def analysis_handler(blog_text: str, remove_freq1: bool, include_title: bool, direct_keyword_input: str, direct_keyword_only: bool):
"""๋ถ„์„ ํ•ธ๋“ค๋Ÿฌ - ํด๋ผ์ด์–ธํŠธ API ํ˜ธ์ถœ"""
debug_log("๋ถ„์„ ํ•ธ๋“ค๋Ÿฌ ํ•จ์ˆ˜ ์‹œ์ž‘")
if not client:
return None, None
try:
result = client.predict(
blog_text,
remove_freq1,
include_title,
direct_keyword_input,
direct_keyword_only,
api_name="/analysis_handler"
)
debug_log("๋ถ„์„ ์ฒ˜๋ฆฌ ์™„๋ฃŒ")
return result
except Exception as e:
debug_log(f"๋ถ„์„ ์ฒ˜๋ฆฌ ์˜ค๋ฅ˜: {str(e)}")
return None, None
# --- ์Šคํฌ๋ž˜ํ•‘ ์‹คํ–‰ ํ•จ์ˆ˜ ---
def fetch_blog_content(url: str):
"""๋ธ”๋กœ๊ทธ ์ฝ˜ํ…์ธ  ๊ฐ€์ ธ์˜ค๊ธฐ"""
debug_log("๋ธ”๋กœ๊ทธ ์ฝ˜ํ…์ธ  ๊ฐ€์ ธ์˜ค๊ธฐ ์‹œ์ž‘")
content = scrape_naver_blog(url)
debug_log("๋ธ”๋กœ๊ทธ ์ฝ˜ํ…์ธ  ๊ฐ€์ ธ์˜ค๊ธฐ ์™„๋ฃŒ")
return content
# --- Gradio ์ธํ„ฐํŽ˜์ด์Šค ๊ตฌ์„ฑ ---
def create_interface():
css = """
/* ============================================
๋‹คํฌ๋ชจ๋“œ ์ž๋™ ๋ณ€๊ฒฝ ํ…œํ”Œ๋ฆฟ CSS
๋‹ค๋ฅธ ํ”„๋กœ์ ํŠธ์— ๋ณต์‚ฌํ•ด์„œ ์‚ฌ์šฉ ๊ฐ€๋Šฅ
============================================ */
/* 1. CSS ๋ณ€์ˆ˜ ์ •์˜ (๋ผ์ดํŠธ๋ชจ๋“œ - ๊ธฐ๋ณธ๊ฐ’) */
:root {
/* ๋ฉ”์ธ ์ปฌ๋Ÿฌ */
--primary-color: #FB7F0D;
--secondary-color: #ff9a8b;
--accent-color: #FF6B6B;
/* ๋ฐฐ๊ฒฝ ์ปฌ๋Ÿฌ */
--background-color: #FFFFFF;
--card-bg: #ffffff;
--input-bg: #ffffff;
/* ํ…์ŠคํŠธ ์ปฌ๋Ÿฌ */
--text-color: #334155;
--text-secondary: #64748b;
/* ๋ณด๋” ๋ฐ ๊ตฌ๋ถ„์„  */
--border-color: #dddddd;
--border-light: #e5e5e5;
/* ํ…Œ์ด๋ธ” ์ปฌ๋Ÿฌ */
--table-even-bg: #f3f3f3;
--table-hover-bg: #f0f0f0;
/* ๊ทธ๋ฆผ์ž */
--shadow: 0 8px 30px rgba(251, 127, 13, 0.08);
--shadow-light: 0 2px 4px rgba(0, 0, 0, 0.1);
/* ๊ธฐํƒ€ */
--border-radius: 18px;
}
/* 2. ๋‹คํฌ๋ชจ๋“œ ์ƒ‰์ƒ ๋ณ€์ˆ˜ (์ž๋™ ๊ฐ์ง€) */
@media (prefers-color-scheme: dark) {
:root {
/* ๋ฐฐ๊ฒฝ ์ปฌ๋Ÿฌ */
--background-color: #1a1a1a;
--card-bg: #2d2d2d;
--input-bg: #2d2d2d;
/* ํ…์ŠคํŠธ ์ปฌ๋Ÿฌ */
--text-color: #e5e5e5;
--text-secondary: #a1a1aa;
/* ๋ณด๋” ๋ฐ ๊ตฌ๋ถ„์„  */
--border-color: #404040;
--border-light: #525252;
/* ํ…Œ์ด๋ธ” ์ปฌ๋Ÿฌ */
--table-even-bg: #333333;
--table-hover-bg: #404040;
/* ๊ทธ๋ฆผ์ž */
--shadow: 0 8px 30px rgba(0, 0, 0, 0.3);
--shadow-light: 0 2px 4px rgba(0, 0, 0, 0.2);
}
}
/* 3. ์ˆ˜๋™ ๋‹คํฌ๋ชจ๋“œ ํด๋ž˜์Šค (Gradio ํ† ๊ธ€์šฉ) */
[data-theme="dark"],
.dark,
.gr-theme-dark {
/* ๋ฐฐ๊ฒฝ ์ปฌ๋Ÿฌ */
--background-color: #1a1a1a;
--card-bg: #2d2d2d;
--input-bg: #2d2d2d;
/* ํ…์ŠคํŠธ ์ปฌ๋Ÿฌ */
--text-color: #e5e5e5;
--text-secondary: #a1a1aa;
/* ๋ณด๋” ๋ฐ ๊ตฌ๋ถ„์„  */
--border-color: #404040;
--border-light: #525252;
/* ํ…Œ์ด๋ธ” ์ปฌ๋Ÿฌ */
--table-even-bg: #333333;
--table-hover-bg: #404040;
/* ๊ทธ๋ฆผ์ž */
--shadow: 0 8px 30px rgba(0, 0, 0, 0.3);
--shadow-light: 0 2px 4px rgba(0, 0, 0, 0.2);
}
/* 4. ๊ธฐ๋ณธ ์š”์†Œ ๋‹คํฌ๋ชจ๋“œ ์ ์šฉ */
body {
font-family: 'Pretendard', 'Noto Sans KR', -apple-system, BlinkMacSystemFont, sans-serif;
background-color: var(--background-color) !important;
color: var(--text-color) !important;
line-height: 1.6;
transition: background-color 0.3s ease, color 0.3s ease;
}
footer {
visibility: hidden;
}
/* 5. Gradio ์ปจํ…Œ์ด๋„ˆ ๊ฐ•์ œ ์ ์šฉ */
.gradio-container,
.gradio-container *,
.gr-app,
.gr-app *,
.gr-interface {
background-color: var(--background-color) !important;
color: var(--text-color) !important;
}
/* 6. ์นด๋“œ ๋ฐ ํŒจ๋„ ์Šคํƒ€์ผ */
.gr-form,
.gr-box,
.gr-panel,
.custom-frame,
[class*="frame"],
[class*="card"],
[class*="panel"] {
background-color: var(--card-bg) !important;
border-color: var(--border-color) !important;
color: var(--text-color) !important;
box-shadow: var(--shadow) !important;
}
/* 7. ์ž…๋ ฅ ํ•„๋“œ ์Šคํƒ€์ผ */
input[type="text"],
input[type="number"],
input[type="email"],
input[type="password"],
textarea,
select,
.gr-input,
.gr-text-input,
.gr-textarea,
.gr-dropdown {
background-color: var(--input-bg) !important;
color: var(--text-color) !important;
border-color: var(--border-color) !important;
}
input[type="text"]:focus,
input[type="number"]:focus,
input[type="email"]:focus,
input[type="password"]:focus,
textarea:focus,
select:focus,
.gr-input:focus,
.gr-text-input:focus,
.gr-textarea:focus,
.gr-dropdown:focus {
border-color: var(--primary-color) !important;
box-shadow: 0 0 0 2px rgba(251, 127, 13, 0.2) !important;
}
/* 8. ๋ผ๋ฒจ ๋ฐ ํ…์ŠคํŠธ ์š”์†Œ */
label,
.gr-label,
.gr-checkbox label,
.gr-radio label,
p, span, div {
color: var(--text-color) !important;
}
/* ํผ ๋ผ๋ฒจ ํ…์ŠคํŠธ ํฌ๊ธฐ ๋Œ€ํญ ์ฆ๊ฐ€ */
.gr-form label,
.gr-textbox label,
.gr-checkbox label,
.gr-radio label {
font-size: 20px !important;
font-weight: 600 !important;
color: var(--text-color) !important;
}
/* ์„ค๋ช… ํ…์ŠคํŠธ ํฌ๊ธฐ ๋Œ€ํญ ์ฆ๊ฐ€ */
.gr-form .gr-form-label,
.gr-textbox .gr-form-label,
.gr-checkbox .gr-form-label,
.gr-radio .gr-form-label,
.gr-info {
font-size: 18px !important;
color: var(--text-secondary) !important;
line-height: 1.4 !important;
}
/* 9. ํ…Œ์ด๋ธ” ์Šคํƒ€์ผ */
table {
background-color: var(--card-bg) !important;
color: var(--text-color) !important;
border-color: var(--border-color) !important;
}
table th {
background-color: var(--primary-color) !important;
color: white !important;
border-color: var(--border-color) !important;
}
table td {
background-color: var(--card-bg) !important;
color: var(--text-color) !important;
border-color: var(--border-color) !important;
}
table tbody tr:nth-child(even) {
background-color: var(--table-even-bg) !important;
}
table tbody tr:hover {
background-color: var(--table-hover-bg) !important;
}
/* 10. ์ฒดํฌ๋ฐ•์Šค ๋ฐ ๋ผ๋””์˜ค ๋ฒ„ํŠผ ์Šคํƒ€์ผ ๊ฐœ์„  */
input[type="checkbox"],
input[type="radio"] {
accent-color: var(--primary-color) !important;
width: 24px !important;
height: 24px !important;
cursor: pointer !important;
}
/* ์ฒดํฌ๋ฐ•์Šค ์ปค์Šคํ…€ ์Šคํƒ€์ผ - ๋” ๊ฐ•๋ ฅํ•œ ์„ ํƒ์ž ์‚ฌ์šฉ */
.gradio-container input[type="checkbox"],
.gr-checkbox input[type="checkbox"],
input[type="checkbox"] {
-webkit-appearance: none !important;
-moz-appearance: none !important;
appearance: none !important;
width: 24px !important;
height: 24px !important;
border: 2px solid var(--border-color) !important;
border-radius: 6px !important;
background-color: var(--card-bg) !important;
cursor: pointer !important;
position: relative !important;
transition: all 0.3s ease !important;
margin-right: 12px !important;
flex-shrink: 0 !important;
}
.gradio-container input[type="checkbox"]:checked,
.gr-checkbox input[type="checkbox"]:checked,
input[type="checkbox"]:checked {
background-color: var(--primary-color) !important;
border-color: var(--primary-color) !important;
}
.gradio-container input[type="checkbox"]:checked::before,
.gr-checkbox input[type="checkbox"]:checked::before,
input[type="checkbox"]:checked::before {
content: "โœ“" !important;
position: absolute !important;
top: 50% !important;
left: 50% !important;
transform: translate(-50%, -50%) !important;
color: white !important;
font-size: 16px !important;
font-weight: bold !important;
line-height: 1 !important;
}
.gradio-container input[type="checkbox"]:hover,
.gr-checkbox input[type="checkbox"]:hover,
input[type="checkbox"]:hover {
border-color: var(--primary-color) !important;
box-shadow: 0 0 0 2px rgba(251, 127, 13, 0.2) !important;
}
/* ์ฒดํฌ๋ฐ•์Šค ๋ผ๋ฒจ ํ…์ŠคํŠธ ํฌ๊ธฐ ๋Œ€ํญ ์ฆ๊ฐ€ */
.gradio-container .gr-checkbox label,
.gr-checkbox label,
label[for] {
font-size: 20px !important;
font-weight: 600 !important;
color: var(--text-color) !important;
cursor: pointer !important;
margin-left: 8px !important;
display: flex !important;
align-items: center !important;
}
/* ์ฒดํฌ๋ฐ•์Šค ์ปจํ…Œ์ด๋„ˆ ์ •๋ ฌ */
.gr-checkbox {
display: flex !important;
align-items: center !important;
gap: 8px !important;
}
/* 11. ์Šคํฌ๋กค๋ฐ” ์Šคํƒ€์ผ */
::-webkit-scrollbar {
width: 8px;
height: 8px;
}
::-webkit-scrollbar-track {
background: var(--card-bg);
border-radius: 10px;
}
::-webkit-scrollbar-thumb {
background: var(--primary-color);
border-radius: 10px;
}
::-webkit-scrollbar-thumb:hover {
background: var(--secondary-color);
}
/* 12. ์•„์ฝ”๋””์–ธ ๋ฐ ๋“œ๋กญ๋‹ค์šด */
details {
background-color: var(--card-bg) !important;
border-color: var(--border-color) !important;
color: var(--text-color) !important;
}
details summary {
background-color: var(--card-bg) !important;
color: var(--text-color) !important;
}
/* 13. ํˆดํŒ ๋ฐ ํŒ์—… */
[data-tooltip]:hover::after,
.tooltip,
.popup {
background-color: var(--card-bg) !important;
color: var(--text-color) !important;
border-color: var(--border-color) !important;
box-shadow: var(--shadow-light) !important;
}
/* 14. ๋ชจ๋‹ฌ ๋ฐ ์˜ค๋ฒ„๋ ˆ์ด */
.modal,
.overlay,
[class*="modal"],
[class*="overlay"] {
background-color: var(--card-bg) !important;
color: var(--text-color) !important;
border-color: var(--border-color) !important;
}
/* 15. ์ถ”๊ฐ€ Gradio ์ปดํฌ๋„ŒํŠธ๋“ค */
.gr-block,
.gr-group,
.gr-row,
.gr-column {
background-color: var(--background-color) !important;
color: var(--text-color) !important;
}
/* 16. ๋ฒ„ํŠผ์€ ๊ธฐ์กด ์Šคํƒ€์ผ ์œ ์ง€ (primary-color ์‚ฌ์šฉ) */
button:not([class*="custom"]):not([class*="primary"]):not([class*="secondary"]) {
background-color: var(--card-bg) !important;
color: var(--text-color) !important;
border-color: var(--border-color) !important;
}
/* 17. ์ฝ”๋“œ ๋ธ”๋ก ๋ฐ pre ํƒœ๊ทธ */
code,
pre,
.code-block {
background-color: var(--table-even-bg) !important;
color: var(--text-color) !important;
border-color: var(--border-color) !important;
}
/* 18. ์•Œ๋ฆผ ๋ฐ ๋ฉ”์‹œ์ง€ */
.alert,
.message,
.notification,
[class*="alert"],
[class*="message"],
[class*="notification"] {
background-color: var(--card-bg) !important;
color: var(--text-color) !important;
border-color: var(--border-color) !important;
}
/* 19. ์ „ํ™˜ ์• ๋‹ˆ๋ฉ”์ด์…˜ */
* {
transition: background-color 0.3s ease,
color 0.3s ease,
border-color 0.3s ease !important;
}
/* 20. ๊ธฐ์กด ์Šคํƒ€์ผ ์œ ์ง€ */
.container {
max-width: 1200px;
margin: 0 auto;
}
.header {
background: linear-gradient(135deg, #FB7F0D, #FF9A5B);
padding: 2rem;
border-radius: 15px;
margin-bottom: 20px;
box-shadow: var(--shadow);
text-align: center;
color: white;
}
.header h1 {
margin: 0;
font-size: 2.5rem;
font-weight: 700;
}
.header p {
margin: 10px 0 0;
font-size: 1.2rem;
opacity: 0.9;
}
.card {
background-color: var(--card-bg);
border-radius: var(--border-radius);
padding: 20px;
margin: 10px 0;
box-shadow: var(--shadow);
border: 1px solid var(--border-color);
}
.button-primary {
border-radius: 30px !important;
background: linear-gradient(135deg, var(--primary-color), var(--secondary-color)) !important;
color: white !important;
font-size: 18px !important;
padding: 10px 20px !important;
border: none;
box-shadow: 0 4px 8px rgba(251, 127, 13, 0.25);
transition: transform 0.3s ease;
text-align: center;
font-weight: 600;
}
.button-primary:hover {
transform: translateY(-2px);
box-shadow: 0 6px 12px rgba(251, 127, 13, 0.3);
}
.section-title {
display: flex;
align-items: center;
font-size: 20px;
font-weight: 700;
color: var(--text-color);
margin-bottom: 15px;
padding-bottom: 8px;
border-bottom: 2px solid var(--primary-color);
}
.section-title i {
margin-right: 10px;
color: var(--primary-color);
}
.guide-container {
background-color: var(--card-bg);
border-radius: var(--border-radius);
padding: 1.5rem;
margin-bottom: 1.5rem;
border: 1px solid var(--border-color);
}
.guide-title {
font-size: 1.3rem;
font-weight: 700;
color: var(--primary-color);
margin-bottom: 1rem;
display: flex;
align-items: center;
}
.guide-title i {
margin-right: 0.8rem;
font-size: 1.3rem;
}
.guide-item {
display: flex;
margin-bottom: 0.8rem;
align-items: flex-start;
}
.guide-number {
background-color: var(--primary-color);
color: white;
width: 24px;
height: 24px;
border-radius: 50%;
display: flex;
align-items: center;
justify-content: center;
font-weight: bold;
margin-right: 10px;
flex-shrink: 0;
font-size: 14px;
}
.guide-text {
flex: 1;
line-height: 1.6;
color: var(--text-color);
}
/* ๊ทธ๋ผ๋””์˜ค ์š”์†Œ ์Šคํƒ€์ผ ์ปค์Šคํ„ฐ๋งˆ์ด์ง• */
.gr-input, .gr-text-input, .gr-textarea {
border-radius: var(--border-radius) !important;
border: 1px solid var(--border-color) !important;
padding: 12px !important;
transition: all 0.3s ease !important;
}
.gr-input:focus, .gr-text-input:focus, .gr-textarea:focus {
border-color: var(--primary-color) !important;
outline: none !important;
box-shadow: 0 0 0 2px rgba(251, 127, 13, 0.2) !important;
}
"""
# FontAwesome ์•„์ด์ฝ˜ ์ถ”๊ฐ€
fontawesome = """
<link rel="stylesheet" href="https://cdnjs.cloudflare.com/ajax/libs/font-awesome/6.0.0/css/all.min.css" crossorigin="anonymous" referrerpolicy="no-referrer" />
"""
with gr.Blocks(css=css, theme=gr.themes.Soft(
primary_hue=gr.themes.Color(
c50="#FFF7ED",
c100="#FFEDD5",
c200="#FED7AA",
c300="#FDBA74",
c400="#FB923C",
c500="#F97316",
c600="#EA580C",
c700="#C2410C",
c800="#9A3412",
c900="#7C2D12",
c950="#431407",
),
secondary_hue="zinc",
neutral_hue="zinc",
font=("Pretendard", "sans-serif")
)) as demo:
gr.HTML(fontawesome)
# ๋ฉ”์ธ ์ปจํ…์ธ 
with gr.Row():
# ์™ผ์ชฝ ์ž…๋ ฅ ์˜์—ญ
with gr.Column(scale=1, elem_classes="card"):
# ๋ธ”๋กœ๊ทธ ๋งํฌ ์ž…๋ ฅ
gr.HTML('<div class="section-title"><i class="fas fa-link"></i> ๋ธ”๋กœ๊ทธ ๋งํฌ ์ž…๋ ฅ</div>')
blog_url_input = gr.Textbox(
label="",
placeholder="๋„ค์ด๋ฒ„ ๋ธ”๋กœ๊ทธ URL์„ ์ž…๋ ฅํ•˜์„ธ์š”...",
lines=1,
)
scrape_button = gr.Button("์Šคํฌ๋ž˜ํ•‘ ์‹คํ–‰", elem_classes="button-primary")
# ๋ธ”๋กœ๊ทธ ๋‚ด์šฉ
gr.HTML('<div class="section-title"><i class="fas fa-file-text"></i> ๋ธ”๋กœ๊ทธ ๋‚ด์šฉ</div>')
blog_content_box = gr.Textbox(
label="",
placeholder="์Šคํฌ๋ž˜ํ•‘๋œ ๋ธ”๋กœ๊ทธ ๋‚ด์šฉ์ด ์—ฌ๊ธฐ์— ํ‘œ์‹œ๋ฉ๋‹ˆ๋‹ค...",
lines=10,
)
# ๋ถ„์„ ์˜ต์…˜
gr.HTML('<div class="section-title"><i class="fas fa-cogs"></i> ๋ถ„์„ ์˜ต์…˜</div>')
with gr.Row():
remove_freq_checkbox = gr.Checkbox(
label="๋นˆ๋„์ˆ˜1 ์ œ๊ฑฐ",
value=True,
info="๋นˆ๋„์ˆ˜๊ฐ€ 1์ธ ๋‹จ์–ด๋“ค์„ ๋ถ„์„ ๊ฒฐ๊ณผ์—์„œ ์ œ์™ธํ•ฉ๋‹ˆ๋‹ค"
)
include_title_checkbox = gr.Checkbox(
label="์ œ๋ชฉํฌํ•จ๊ธฐ๋Šฅ",
value=True,
info="๋ธ”๋กœ๊ทธ ์ œ๋ชฉ์„ ํฌํ•จํ•˜์—ฌ ๋ถ„์„์„ ์ˆ˜ํ–‰ํ•ฉ๋‹ˆ๋‹ค"
)
with gr.Row():
direct_keyword_only_checkbox = gr.Checkbox(
label="์ง์ ‘ ํ‚ค์›Œ๋“œ ์ž…๋ ฅ๋งŒ ๋ถ„์„",
value=False,
info="ํ˜•ํƒœ์†Œ ๋ถ„์„ ์—†์ด ์ง์ ‘ ์ž…๋ ฅํ•œ ํ‚ค์›Œ๋“œ๋งŒ ๋ถ„์„ํ•ฉ๋‹ˆ๋‹ค"
)
# ์ง์ ‘ ํ‚ค์›Œ๋“œ ์ž…๋ ฅ
direct_keyword_box = gr.Textbox(
label="์ง์ ‘ ํ‚ค์›Œ๋“œ ์ž…๋ ฅ (์—”ํ„ฐ ๋˜๋Š” ','๋กœ ๊ตฌ๋ถ„)",
placeholder="ํ‚ค์›Œ๋“œ1, ํ‚ค์›Œ๋“œ2, ํ‚ค์›Œ๋“œ3...",
lines=2,
)
analyze_button = gr.Button("๋ถ„์„ ์‹คํ–‰", elem_classes="button-primary")
# ์˜ค๋ฅธ์ชฝ ์ถœ๋ ฅ ์˜์—ญ
with gr.Column(scale=1, elem_classes="card"):
# ๋ถ„์„ ๊ฒฐ๊ณผ
gr.HTML('<div class="section-title"><i class="fas fa-chart-bar"></i> ๋ถ„์„ ๊ฒฐ๊ณผ</div>')
result_df = gr.Dataframe(
label="",
interactive=True,
)
# ํŒŒ์ผ ๋‹ค์šด๋กœ๋“œ
gr.HTML('<div class="section-title"><i class="fas fa-download"></i> ๊ฒฐ๊ณผ ๋‹ค์šด๋กœ๋“œ</div>')
excel_file = gr.File(label="", file_types=[".xlsx"])
# ์ด๋ฒคํŠธ ์—ฐ๊ฒฐ
scrape_button.click(fn=fetch_blog_content, inputs=blog_url_input, outputs=blog_content_box)
analyze_button.click(fn=analysis_handler,
inputs=[blog_content_box, remove_freq_checkbox, include_title_checkbox, direct_keyword_box, direct_keyword_only_checkbox],
outputs=[result_df, excel_file])
return demo
if __name__ == "__main__":
debug_log("Gradio ์•ฑ ์‹คํ–‰ ์‹œ์ž‘")
# ํด๋ผ์ด์–ธํŠธ ์—ฐ๊ฒฐ ์ƒํƒœ ํ™•์ธ (์—”๋“œํฌ์ธํŠธ ์ •๋ณด๋Š” ๋กœ๊ทธ์— ์ถœ๋ ฅํ•˜์ง€ ์•Š์Œ)
if not client:
print("๊ฒฝ๊ณ : ํด๋ผ์ด์–ธํŠธ ์—ฐ๊ฒฐ์— ์‹คํŒจํ–ˆ์Šต๋‹ˆ๋‹ค. API_ENDPOINT ํ™˜๊ฒฝ๋ณ€์ˆ˜๋ฅผ ํ™•์ธํ•˜์„ธ์š”.")
else:
debug_log("ํด๋ผ์ด์–ธํŠธ ์—ฐ๊ฒฐ ์„ฑ๊ณต")
demo = create_interface()
demo.queue()
demo.launch()