GGGISfolio / app.py
miniwa00's picture
μ•± UI κ°œμ„ : 'About' νƒ­ μΆ”κ°€ 및 λ…Έλ“œ μž…λ ₯, 확인 κΈ°λŠ₯의 μƒνƒœ μ΄ˆκΈ°ν™” 둜직 κ΅¬ν˜„. λ…Έλ“œ μž…λ ₯ν•˜κΈ° νƒ­μ—μ„œ 아이디어 생성 μƒνƒœ μ΄ˆκΈ°ν™” κΈ°λŠ₯ μΆ”κ°€.
26f4002
Raw
History Blame Contribute Delete
22 kB
import gradio as gr
from src.data_manager import initialize_data
from src.node_functions import (
get_nodes_dataframe,
add_keyword,
create_node,
filter_nodes_multi,
)
from src.idea_functions import (
get_ideas_dataframe,
generate_idea_with_chatgpt,
generate_idea_with_gemini,
)
from src.ui_handlers import (
refresh_and_reset,
handle_idea_selection,
handle_delete_idea,
handle_node_selection,
handle_edit_node,
handle_delete_node,
filter_ideas,
refresh_and_clear_status,
refresh_idea_nodes,
)
from src.about_content import get_about_content
# μ•± μ‹œμž‘ μ‹œ 데이터 μ΄ˆκΈ°ν™”
initialize_data()
# Gradio μΈν„°νŽ˜μ΄μŠ€ ꡬ성
with gr.Blocks(title="", theme=gr.themes.Soft()) as demo:
# 헀더
gr.Markdown("# πŸ”— GGG Idea Studio")
gr.Markdown("---")
# νƒ­ ꡬ성
with gr.Tabs():
# 1. About νƒ­
with gr.Tab("🌟 About") as about_tab:
gr.Markdown(get_about_content())
# 2. λ…Έλ“œ μž…λ ₯ν•˜κΈ° νƒ­
with gr.Tab("✏️ λ…Έλ“œ μž…λ ₯ν•˜κΈ°") as node_input_tab:
gr.Markdown("### λ…Έλ“œλ₯Ό μƒμ„±ν•˜μ„Έμš”")
with gr.Column():
title_input = gr.Textbox(
label="1. λ…Έλ“œ 제λͺ©", placeholder="λ…Έλ“œ 제λͺ©μ„ μž…λ ₯ν•˜μ„Έμš”"
)
description_input = gr.Textbox(
label="2. λ…Έλ“œμ— λŒ€ν•΄ μ†Œκ°œν•΄μ£Όμ„Έμš”. (핡심기λŠ₯κ³Ό μ‚¬μš© κΈ°μˆ μ„ ν¬ν•¨ν•˜μ—¬ μž‘μ„±ν•΄μ£Όμ„Έμš”.)",
lines=5,
placeholder="λ…Έλ“œμ˜ 핡심 κΈ°λŠ₯κ³Ό μ‚¬μš©λœ κΈ°μˆ μ„ ν¬ν•¨ν•˜μ—¬ μžμ„Ένžˆ μ„€λͺ…ν•΄μ£Όμ„Έμš”",
)
tenant_input = gr.Textbox(
label="3. ν…Œλ„ŒνŠΈ (κ·Έλ£Ή)",
placeholder="이 λ…Έλ“œκ°€ 속할 그룹을 μž…λ ₯ν•˜μ„Έμš” (예: κ΅­λ―ΌλŒ€, SuperbAI)",
)
with gr.Row():
keyword_input = gr.Textbox(
label="ν‚€μ›Œλ“œ",
placeholder="ν‚€μ›Œλ“œλ₯Ό μž…λ ₯ν•˜μ„Έμš” (예: 파이썬, 인곡지λŠ₯, AWS)",
scale=2,
)
add_keyword_btn = gr.Button(
"βž• ν‚€μ›Œλ“œ μΆ”κ°€",
variant="secondary",
scale=1,
)
tags_display = gr.Textbox(
label="μΆ”κ°€λœ ν‚€μ›Œλ“œ",
interactive=False,
placeholder="ν‚€μ›Œλ“œκ°€ 여기에 ν‘œμ‹œλ©λ‹ˆλ‹€",
)
keyword_status = gr.Textbox(label="ν‚€μ›Œλ“œ μΆ”κ°€ μƒνƒœ", interactive=False)
create_btn = gr.Button("✨ λ…Έλ“œ μƒμ„±ν•˜κΈ°", variant="primary", size="lg")
create_status = gr.Textbox(label="생성 κ²°κ³Ό", interactive=False)
# 이벀트 μ—°κ²°
add_keyword_btn.click(
add_keyword,
inputs=[keyword_input, tags_display],
outputs=[tags_display, keyword_input, keyword_status],
)
# μ—”ν„°ν‚€λ‘œλ„ ν‚€μ›Œλ“œ μΆ”κ°€ κ°€λŠ₯
keyword_input.submit(
add_keyword,
inputs=[keyword_input, tags_display],
outputs=[tags_display, keyword_input, keyword_status],
)
create_btn.click(
create_node,
inputs=[title_input, description_input, tenant_input, tags_display],
outputs=[
create_status,
title_input,
description_input,
tenant_input,
keyword_input,
tags_display,
keyword_status,
],
)
# 3. λ‚΄ λ…Έλ“œ ν™•μΈν•˜κΈ° νƒ­
with gr.Tab("πŸ“‹ λ‚΄ λ…Έλ“œ ν™•μΈν•˜κΈ°") as node_view_tab:
gr.Markdown("### μƒμ„±λœ λͺ¨λ“  λ…Έλ“œλ₯Ό ν™•μΈν•˜κ³  κ΄€λ¦¬ν•˜μ„Έμš”")
# 필터링 μ˜΅μ…˜λ“€
with gr.Row():
search_input = gr.Textbox(
label="πŸ” λ…Έλ“œ 이름 검색",
placeholder="검색할 λ…Έλ“œ 이름을 μž…λ ₯ν•˜μ„Έμš”...",
scale=2,
)
with gr.Row():
tenant_filter = gr.Dropdown(
label="🏒 ν…Œλ„ŒνŠΈ ν•„ν„° (닀쀑선택)",
choices=[], # μ΄ˆκΈ°μ—λŠ” 빈 리슀트
multiselect=True,
scale=1,
)
tag_filter = gr.Dropdown(
label="🏷️ νƒœκ·Έ ν•„ν„° (닀쀑선택)",
choices=[], # μ΄ˆκΈ°μ—λŠ” 빈 리슀트
multiselect=True,
scale=1,
)
nodes_dataframe = gr.Dataframe(
value=get_nodes_dataframe(),
headers=["μƒμ„±μΌμž", "λ…Έλ“œ 이름", "ν…Œλ„ŒνŠΈ", "μ„€λͺ…", "νƒœκ·Έ"],
interactive=False,
wrap=False,
elem_id="nodes_table",
)
# μ„ νƒλœ λ…Έλ“œ 상세 정보
gr.Markdown("#### πŸ“‹ μ„ νƒλœ λ…Έλ“œ 상세 정보")
gr.Markdown(
"*μœ„ ν…Œμ΄λΈ”μ—μ„œ 행을 ν΄λ¦­ν•˜λ©΄ ν•΄λ‹Ή λ…Έλ“œμ˜ 상세 정보가 ν‘œμ‹œλ©λ‹ˆλ‹€.*"
)
with gr.Accordion("πŸ”§ λ…Έλ“œ 상세 정보", open=True):
selected_node_title = gr.Textbox(label="λ…Έλ“œ 제λͺ©", interactive=True)
selected_node_description = gr.Textbox(
label="λ…Έλ“œ μ„€λͺ…", lines=4, interactive=True
)
selected_node_tenant = gr.Textbox(label="ν…Œλ„ŒνŠΈ", interactive=True)
selected_node_tags = gr.Textbox(
label="νƒœκ·Έ (콀마둜 ꡬ뢄)", interactive=True
)
selected_node_created_at = gr.Textbox(
label="μƒμ„±μΌμ‹œ", interactive=False
)
# νŽΈμ§‘/μ‚­μ œ κ΄€λ ¨ UI
selected_node_index = gr.State(-1) # μ„ νƒλœ λ…Έλ“œ 인덱슀
with gr.Row():
edit_node_btn = gr.Button(
"✏️ μ„ νƒλœ λ…Έλ“œ νŽΈμ§‘", variant="primary", visible=False
)
delete_node_btn = gr.Button(
"πŸ—‘οΈ μ„ νƒλœ λ…Έλ“œ μ‚­μ œ", variant="stop", visible=False
)
node_action_status = gr.Textbox(
label="", interactive=False, visible=False, show_label=False
)
# 이벀트 μ—°κ²° - λͺ¨λ“  ν•„ν„° λ³€κ²½ μ‹œ μ‹€μ‹œκ°„ 필터링
for filter_component in [search_input, tenant_filter, tag_filter]:
filter_component.change(
filter_nodes_multi,
inputs=[search_input, tenant_filter, tag_filter],
outputs=[nodes_dataframe],
)
# λ…Έλ“œ 선택 이벀트
nodes_dataframe.select(
fn=handle_node_selection,
outputs=[
selected_node_title,
selected_node_description,
selected_node_tenant,
selected_node_tags,
selected_node_created_at,
selected_node_index,
edit_node_btn,
delete_node_btn,
node_action_status,
],
)
# λ…Έλ“œ νŽΈμ§‘ 이벀트
edit_node_btn.click(
fn=handle_edit_node,
inputs=[
selected_node_index,
selected_node_title,
selected_node_description,
selected_node_tenant,
selected_node_tags,
],
outputs=[
node_action_status,
nodes_dataframe,
],
)
# λ…Έλ“œ μ‚­μ œ 이벀트
delete_node_btn.click(
fn=handle_delete_node,
inputs=[selected_node_index],
outputs=[
node_action_status,
nodes_dataframe,
edit_node_btn,
delete_node_btn,
selected_node_title,
selected_node_description,
selected_node_tenant,
selected_node_tags,
selected_node_created_at,
],
)
# 4. AI 아이디어 생성 νƒ­
with gr.Tab("πŸš€ AI 아이디어 생성") as idea_generation_tab:
gr.Markdown("### AI 아이디어λ₯Ό μƒμ„±ν•˜μ„Έμš”")
# 곡λͺ¨μ „ 정보 μ„Ήμ…˜
contest_title = gr.Textbox(
label="곡λͺ¨μ „ 제λͺ©", placeholder="μ°Έκ°€ν•  곡λͺ¨μ „ 제λͺ©μ„ μž…λ ₯ν•˜μ„Έμš”"
)
contest_theme = gr.Textbox(
label="곡λͺ¨μ „ 주제 [Domain]",
placeholder="곡λͺ¨μ „μ˜ μ£Όμš” 주제 ν˜Ήμ€ 곡λͺ¨μ „μ˜ 도메인을 μž…λ ₯ν•˜μ„Έμš”",
)
contest_description = gr.Textbox(
label="곡λͺ¨μ „ μ„€λͺ… [Context]",
lines=3,
placeholder="곡λͺ¨μ „에 λŒ€ν•œ 상세 μ„€λͺ…κ³Ό 좔가적인 λ§₯락 정보λ₯Ό μž…λ ₯ν•˜μ„Έμš”",
)
contest_context = gr.Textbox(
label="곡λͺ¨μ „ λ§₯락 [Igniter] (선택사항)",
lines=2,
placeholder="μ•„μ΄λ””μ—μ΄μ…˜μ˜ λ°©ν–₯성을 κ²°μ •ν•˜λŠ” 핡심 포인트λ₯Ό μž…λ ₯ν•˜μ„Έμš” (선택사항)",
)
# λ…Έλ“œ μ—°κ²° μ„Ήμ…˜
gr.Markdown("#### πŸ”— λ…Έλ“œ μ—°κ²°")
gr.Markdown("*아이디어 생성에 μ‚¬μš©ν•  λ…Έλ“œλ“€μ„ ν•„ν„°λ§ν•˜μ—¬ μ„ νƒν•˜μ„Έμš”*")
# λ…Έλ“œ 필터링 μ˜΅μ…˜λ“€
with gr.Row():
idea_node_search_input = gr.Textbox(
label="πŸ” λ…Έλ“œ 이름 검색",
placeholder="검색할 λ…Έλ“œ 이름을 μž…λ ₯ν•˜μ„Έμš”...",
scale=2,
)
with gr.Row():
idea_tenant_filter = gr.Dropdown(
label="🏒 ν…Œλ„ŒνŠΈ ν•„ν„° (닀쀑선택)",
choices=[], # μ΄ˆκΈ°μ—λŠ” 빈 리슀트
multiselect=True,
scale=1,
)
idea_tag_filter = gr.Dropdown(
label="🏷️ νƒœκ·Έ ν•„ν„° (닀쀑선택)",
choices=[], # μ΄ˆκΈ°μ—λŠ” 빈 리슀트
multiselect=True,
scale=1,
)
idea_nodes_dataframe = gr.Dataframe(
value=get_nodes_dataframe(),
headers=["μƒμ„±μΌμž", "λ…Έλ“œ 이름", "ν…Œλ„ŒνŠΈ", "μ„€λͺ…", "νƒœκ·Έ"],
interactive=False,
wrap=False,
elem_id="idea_nodes_table",
)
# 아이디어 생성 μ„Ήμ…˜
gr.Markdown("#### πŸ€– 아이디어 μƒμ„±ν•˜κΈ°")
with gr.Row():
chatgpt_btn = gr.Button(
"πŸ€– ChatGPT둜 아이디어 생성", variant="primary", size="lg"
)
gemini_btn = gr.Button(
"πŸ’Ž Gemini둜 생성 (쀀비쀑)",
variant="secondary",
size="lg",
interactive=False,
)
idea_generation_status = gr.Textbox(label="생성 μƒνƒœ", interactive=False)
# 이벀트 μ—°κ²°
# λ…Έλ“œ 필터링 μ΄λ²€νŠΈλ“€
for filter_component in [
idea_node_search_input,
idea_tenant_filter,
idea_tag_filter,
]:
filter_component.change(
filter_nodes_multi,
inputs=[
idea_node_search_input,
idea_tenant_filter,
idea_tag_filter,
],
outputs=[idea_nodes_dataframe],
)
chatgpt_btn.click(
generate_idea_with_chatgpt,
inputs=[
contest_title,
contest_theme,
contest_description,
contest_context,
idea_node_search_input,
idea_tenant_filter,
idea_tag_filter,
],
outputs=[
idea_generation_status,
contest_title,
contest_theme,
contest_description,
contest_context,
],
)
gemini_btn.click(
generate_idea_with_gemini,
inputs=[
contest_title,
contest_theme,
contest_description,
contest_context,
idea_node_search_input,
idea_tenant_filter,
idea_tag_filter,
],
outputs=[
idea_generation_status,
contest_title,
contest_theme,
contest_description,
contest_context,
],
)
# νƒ­ ν΄λ¦­μ‹œ μžλ™ μ΄ˆκΈ°ν™” κΈ°λŠ₯ 제거 (μ‚¬μš©μž μš”μ²­)
# 5. μƒμ„±λœ 아이디어 ν™•μΈν•˜κΈ° νƒ­
with gr.Tab("πŸ’­ μƒμ„±λœ 아이디어 ν™•μΈν•˜κΈ°") as ideas_view_tab:
gr.Markdown(
"### AI둜 μƒμ„±λœ 아이디어λ₯Ό ν™•μΈν•˜κ³  상세 λ‚΄μš©μ„ λ³Ό 수 μžˆμŠ΅λ‹ˆλ‹€"
)
# 검색 ν•„λ“œ
idea_search_input = gr.Textbox(
label="πŸ” 곡λͺ¨μ „ 제λͺ© λ˜λŠ” 아이디어 제λͺ© 검색",
placeholder="검색할 제λͺ©μ„ μž…λ ₯ν•˜μ„Έμš”...",
scale=2,
)
# 아이디어 λͺ©λ‘
ideas_dataframe = gr.Dataframe(
value=get_ideas_dataframe(),
headers=[
"μƒμ„±μΌμ‹œ",
"곡λͺ¨μ „ 제λͺ©",
"아이디어 제λͺ©",
"아이디어 κ°œμš”",
"AI 이름",
],
interactive=False,
elem_id="ideas_table",
)
# μ„ νƒλœ 아이디어 상세 정보
gr.Markdown("#### πŸ“‹ μ„ νƒλœ 아이디어 상세 정보")
gr.Markdown(
"*μœ„ ν…Œμ΄λΈ”μ—μ„œ 행을 ν΄λ¦­ν•˜λ©΄ ν•΄λ‹Ή μ•„μ΄λ””μ–΄μ˜ 상세 정보가 ν‘œμ‹œλ©λ‹ˆλ‹€.*"
)
with gr.Accordion("🎯 아이디어 상세 정보", open=True):
selected_title = gr.Textbox(label="아이디어 제λͺ©", interactive=False)
contest_info_display = gr.Textbox(
label="곡λͺ¨μ „ 정보", lines=4, interactive=False
)
problem_display = gr.Textbox(
label="문제 μ˜μ‹", lines=3, interactive=False
)
solution_display = gr.Textbox(
label="μ†”λ£¨μ…˜ ν•΄κ²° λ°©μ•ˆ", lines=4, interactive=False
)
implementation_display = gr.Textbox(
label="κ΅¬ν˜„ λ°©μ•ˆ", lines=3, interactive=False
)
expected_effect_display = gr.Textbox(
label="κΈ°λŒ€ 효과", lines=3, interactive=False
)
created_at_display = gr.Textbox(label="μƒμ„±μΌμ‹œ", interactive=False)
with gr.Accordion("πŸ”— μ‚¬μš©λœ λ…Έλ“œ 및 ν•„ν„° 정보", open=False):
used_nodes_display = gr.Textbox(
label="μ‚¬μš©λœ λ…Έλ“œ", lines=5, interactive=False
)
used_filters_display = gr.Textbox(
label="μ‚¬μš©λœ ν•„ν„°", lines=5, interactive=False
)
rationale_display = gr.Textbox(
label="아이디어 생성 κ·Όκ±° (Connecting the Dots)",
lines=8,
interactive=False,
)
# μ‚­μ œ κ΄€λ ¨ UI
selected_idea_index = gr.State(-1) # μ„ νƒλœ 아이디어 인덱슀
with gr.Row():
delete_idea_btn = gr.Button(
"πŸ—‘οΈ μ„ νƒλœ 아이디어 μ‚­μ œ", variant="stop", visible=False
)
delete_status = gr.Textbox(
label="", interactive=False, visible=False, show_label=False
)
# 이벀트 μ—°κ²°
# 검색 ν•„λ“œ 이벀트
idea_search_input.change(
fn=filter_ideas,
inputs=[idea_search_input],
outputs=[ideas_dataframe],
)
ideas_dataframe.select(
fn=handle_idea_selection,
outputs=[
selected_title,
contest_info_display,
problem_display,
solution_display,
implementation_display,
expected_effect_display,
created_at_display,
used_nodes_display,
used_filters_display,
rationale_display,
selected_idea_index,
delete_idea_btn,
delete_status,
],
)
delete_idea_btn.click(
fn=handle_delete_idea,
inputs=[selected_idea_index],
outputs=[
delete_status,
ideas_dataframe,
delete_idea_btn,
selected_title,
contest_info_display,
problem_display,
solution_display,
implementation_display,
expected_effect_display,
created_at_display,
used_nodes_display,
used_filters_display,
rationale_display,
],
)
# νƒ­ ν΄λ¦­μ‹œ μžλ™ μƒˆλ‘œκ³ μΉ¨ 및 아이디어 생성 μƒνƒœ μ΄ˆκΈ°ν™”
ideas_view_tab.select(
fn=refresh_and_reset,
outputs=[
ideas_dataframe,
delete_idea_btn,
delete_status,
selected_title,
contest_info_display,
problem_display,
solution_display,
implementation_display,
expected_effect_display,
created_at_display,
used_nodes_display,
used_filters_display,
rationale_display,
idea_generation_status, # 아이디어 생성 μƒνƒœ μ΄ˆκΈ°ν™”
idea_search_input, # 검색 ν•„λ“œ μ΄ˆκΈ°ν™”
],
)
# νƒ­ κ°„ μƒνƒœ μ΄ˆκΈ°ν™” 이벀트 (λͺ¨λ“  μ»΄ν¬λ„ŒνŠΈ μ •μ˜ ν›„)
# AI 아이디어 생성 νƒ­ ν΄λ¦­μ‹œ μƒνƒœ μ΄ˆκΈ°ν™” 및 λ…Έλ“œ ν•„ν„° μ΄ˆκΈ°ν™”
idea_generation_tab.select(
fn=refresh_idea_nodes,
outputs=[
idea_nodes_dataframe,
idea_node_search_input,
idea_tenant_filter,
idea_tag_filter,
create_status,
],
)
# λ…Έλ“œ μž…λ ₯ν•˜κΈ° νƒ­ ν΄λ¦­μ‹œ 아이디어 생성 μƒνƒœ, λ…Έλ“œ 생성 μƒνƒœλ§Œ μ΄ˆκΈ°ν™”
node_input_tab.select(
fn=lambda: ("", ""),
outputs=[idea_generation_status, create_status],
)
# λ‚΄ λ…Έλ“œ ν™•μΈν•˜κΈ° νƒ­ ν΄λ¦­μ‹œ μžλ™ μƒˆλ‘œκ³ μΉ¨ 및 μƒνƒœ μ΄ˆκΈ°ν™”
node_view_tab.select(
fn=refresh_and_clear_status,
outputs=[
nodes_dataframe,
search_input,
tenant_filter,
tag_filter,
idea_generation_status,
create_status,
selected_node_title,
selected_node_description,
selected_node_tenant,
selected_node_tags,
selected_node_created_at,
selected_node_index,
edit_node_btn,
delete_node_btn,
node_action_status,
],
)
# AI 아이디어 생성 νƒ­ ν΄λ¦­μ‹œ μƒνƒœ μ΄ˆκΈ°ν™” 및 λ…Έλ“œ ν•„ν„° μ΄ˆκΈ°ν™”
idea_generation_tab.select(
fn=refresh_idea_nodes,
outputs=[
idea_nodes_dataframe,
idea_node_search_input,
idea_tenant_filter,
idea_tag_filter,
create_status,
],
)
# λ…Έλ“œ μž…λ ₯ν•˜κΈ° νƒ­ ν΄λ¦­μ‹œ 아이디어 생성 μƒνƒœ, λ…Έλ“œ 생성 μƒνƒœλ§Œ μ΄ˆκΈ°ν™”
node_input_tab.select(
fn=lambda: ("", ""),
outputs=[idea_generation_status, create_status],
)
# λ‚΄ λ…Έλ“œ ν™•μΈν•˜κΈ° νƒ­ ν΄λ¦­μ‹œ μžλ™ μƒˆλ‘œκ³ μΉ¨ 및 μƒνƒœ μ΄ˆκΈ°ν™”
node_view_tab.select(
fn=refresh_and_clear_status,
outputs=[
nodes_dataframe,
search_input,
tenant_filter,
tag_filter,
idea_generation_status,
create_status,
selected_node_title,
selected_node_description,
selected_node_tenant,
selected_node_tags,
selected_node_created_at,
selected_node_index,
edit_node_btn,
delete_node_btn,
node_action_status,
],
)
# μ•± μ‹€ν–‰
if __name__ == "__main__":
demo.launch(share=False, debug=True, server_name="0.0.0.0", server_port=7860)