Update app.py
Browse files
app.py
CHANGED
@@ -19,6 +19,14 @@ import pandas as pd
|
|
19 |
import chardet
|
20 |
import gradio as gr
|
21 |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
22 |
# μμ ν
νλ¦Ώ μ μ
|
23 |
EXAMPLE_TOPICS = {
|
24 |
"λΉμ¦λμ€ μ μμ": "AI κΈ°λ° κ³ κ° μλΉμ€ μλν νλ«νΌ ν¬μ μ μ",
|
@@ -37,6 +45,7 @@ current_slides_data = []
|
|
37 |
current_topic = ""
|
38 |
current_template = ""
|
39 |
current_theme = ""
|
|
|
40 |
|
41 |
# λμμΈ ν
λ§ μ μ
|
42 |
DESIGN_THEMES = {
|
@@ -107,7 +116,7 @@ DESIGN_THEMES = {
|
|
107 |
}
|
108 |
}
|
109 |
|
110 |
-
# μ€νμΌ μ μ (
|
111 |
STYLE_TEMPLATES = {
|
112 |
"Title Slide (Hero)": {
|
113 |
"name": "Title Slide",
|
@@ -143,7 +152,8 @@ STYLE_TEMPLATES = {
|
|
143 |
"name": "Business Process",
|
144 |
"description": "End-to-end business workflow with clear phases",
|
145 |
"use_case": "νλ‘μΈμ€ μ€λͺ
, λ¨κ³λ³ μ§ν",
|
146 |
-
"example": "A detailed hand-drawn diagram illustrating an end-to-end business workflow with Market Analysis, Strategy Development, Product Design, Implementation, and Post-Launch Review phases. Clear directional arrows, iconography for each component, vibrant educational yet professional style"
|
|
|
147 |
},
|
148 |
"Industrial Design": {
|
149 |
"name": "Product Design",
|
@@ -203,20 +213,22 @@ STYLE_TEMPLATES = {
|
|
203 |
"name": "Flowchart",
|
204 |
"description": "Vibrant flowchart with minimalistic icons",
|
205 |
"use_case": "μμ¬κ²°μ , νλ‘μΈμ€ νλ¦",
|
206 |
-
"example": "FLOWCHART DESIGN: A hand-drawn style flowchart, vibrant colors, minimalistic icons showing process flow from START to END with decision points, branches, and clear directional arrows"
|
|
|
207 |
}
|
208 |
}
|
209 |
|
210 |
# PPT ν
νλ¦Ώ μ μ (λμ μΌλ‘ μ¬λΌμ΄λ μ μ‘°μ κ°λ₯)
|
|
|
211 |
PPT_TEMPLATES = {
|
212 |
"λΉμ¦λμ€ μ μμ": {
|
213 |
"description": "ν¬μ μ μΉ, μ¬μ
μ μμ©",
|
214 |
"core_slides": [
|
215 |
-
{"title": "λͺ©μ°¨", "style": "Flowchart", "prompt_hint": "νλ μ ν
μ΄μ
ꡬ쑰"},
|
216 |
{"title": "λ¬Έμ μ μ", "style": "Colorful Mind Map", "prompt_hint": "νμ¬ μμ₯μ λ¬Έμ μ "},
|
217 |
{"title": "νν© λΆμ", "style": "Elegant SWOT Quadrant", "prompt_hint": "κ°μ , μ½μ , κΈ°ν, μν"},
|
218 |
{"title": "μ루μ
", "style": "Industrial Design", "prompt_hint": "μ ν/μλΉμ€ 컨μ
"},
|
219 |
-
{"title": "νλ‘μΈμ€", "style": "Business Workflow", "prompt_hint": "μ€ν λ¨κ³"},
|
220 |
{"title": "μΌμ ", "style": "Timeline Ribbon", "prompt_hint": "μ£Όμ λ§μΌμ€ν€"}
|
221 |
],
|
222 |
"optional_slides": [
|
@@ -226,8 +238,8 @@ PPT_TEMPLATES = {
|
|
226 |
{"title": "ν μκ°", "style": "Colorful Mind Map", "prompt_hint": "ν΅μ¬ νμκ³Ό μλ"},
|
227 |
{"title": "μ¬λ¬΄ κ³ν", "style": "KPI Dashboard", "prompt_hint": "μμ λ§€μΆκ³Ό μμ΅"},
|
228 |
{"title": "μν κ΄λ¦¬", "style": "Risk Heat Map", "prompt_hint": "μ£Όμ 리μ€ν¬μ λμ"},
|
229 |
-
{"title": "ννΈλμ", "style": "
|
230 |
-
{"title": "κΈ°μ μ€ν", "style": "
|
231 |
{"title": "κ³ κ° μ¬λ‘", "style": "Industrial Design", "prompt_hint": "μ±κ³΅ μ¬λ‘"},
|
232 |
{"title": "μ±μ₯ μ λ΅", "style": "Timeline Ribbon", "prompt_hint": "νμ₯ κ³ν"},
|
233 |
{"title": "ν¬μ νμ©", "style": "Pyramid/Funnel", "prompt_hint": "μκΈ μ¬μ© κ³ν"},
|
@@ -240,7 +252,7 @@ PPT_TEMPLATES = {
|
|
240 |
{"title": "μ ν 컨μ
", "style": "Industrial Design", "prompt_hint": "μ ν λμμΈ"},
|
241 |
{"title": "μ¬μ©μ λμ¦", "style": "Colorful Mind Map", "prompt_hint": "κ³ κ° νμΈν¬μΈνΈ"},
|
242 |
{"title": "κΈ°λ₯ μκ°", "style": "Mobile App Mockup", "prompt_hint": "UI/UX νλ©΄"},
|
243 |
-
{"title": "μλ μ리", "style": "Flowchart", "prompt_hint": "κΈ°λ₯ νλ‘μ°"},
|
244 |
{"title": "μμ₯ ν¬μ§μ
", "style": "3D Bubble Chart", "prompt_hint": "κ²½μμ¬ λΉκ΅"},
|
245 |
{"title": "μΆμ μΌμ ", "style": "Timeline Ribbon", "prompt_hint": "λ°μΉ λ‘λλ§΅"}
|
246 |
],
|
@@ -248,7 +260,7 @@ PPT_TEMPLATES = {
|
|
248 |
{"title": "νκ² κ³ κ°", "style": "Colorful Mind Map", "prompt_hint": "μ£Όμ κ³ κ°μΈ΅"},
|
249 |
{"title": "κ°κ²© μ μ±
", "style": "Pyramid/Funnel", "prompt_hint": "κ°κ²© μ λ΅"},
|
250 |
{"title": "κΈ°μ μ°μ", "style": "Industrial Design", "prompt_hint": "ν΅μ¬ κΈ°μ "},
|
251 |
-
{"title": "μ¬μ© μλ리μ€", "style": "
|
252 |
{"title": "κ³ κ° νκΈ°", "style": "KPI Dashboard", "prompt_hint": "μ¬μ©μ νκ°"},
|
253 |
{"title": "νλ§€ μ±λ", "style": "Value Chain", "prompt_hint": "μ ν΅ μ λ΅"},
|
254 |
{"title": "λ§μΌν
μ λ΅", "style": "Timeline Ribbon", "prompt_hint": "ν보 κ³ν"},
|
@@ -258,7 +270,7 @@ PPT_TEMPLATES = {
|
|
258 |
"νλ‘μ νΈ λ³΄κ³ ": {
|
259 |
"description": "μ§ν μν©, μ±κ³Ό λ³΄κ³ μ©",
|
260 |
"core_slides": [
|
261 |
-
{"title": "νλ‘μ νΈ κ°μ", "style": "Business Workflow", "prompt_hint": "μ 체 νλ‘μΈμ€"},
|
262 |
{"title": "μ§ν νν©", "style": "Gantt Chart", "prompt_hint": "μμ
μΌμ "},
|
263 |
{"title": "리μ€ν¬ κ΄λ¦¬", "style": "Risk Heat Map", "prompt_hint": "μν μμ"},
|
264 |
{"title": "μ±κ³Ό μ§ν", "style": "KPI Dashboard", "prompt_hint": "λ¬μ± μ€μ "},
|
@@ -269,7 +281,7 @@ PPT_TEMPLATES = {
|
|
269 |
{"title": "ν μ±κ³Ό", "style": "3D Bubble Chart", "prompt_hint": "νλ³ κΈ°μ¬λ"},
|
270 |
{"title": "μ΄μ κ΄λ¦¬", "style": "Risk Heat Map", "prompt_hint": "μ£Όμ μ΄μ"},
|
271 |
{"title": "κ°μ μ¬ν", "style": "Colorful Mind Map", "prompt_hint": "νλ‘μΈμ€ κ°μ "},
|
272 |
-
{"title": "κ΅ν", "style": "
|
273 |
]
|
274 |
},
|
275 |
"μ λ΅ κΈ°ν": {
|
@@ -285,8 +297,8 @@ PPT_TEMPLATES = {
|
|
285 |
"optional_slides": [
|
286 |
{"title": "μμ₯ μ λ§", "style": "3D Bubble Chart", "prompt_hint": "λ―Έλ μμ₯"},
|
287 |
{"title": "νμ λ°©ν₯", "style": "Industrial Design", "prompt_hint": "νμ μ λ΅"},
|
288 |
-
{"title": "μ‘°μ§ λ³ν", "style": "
|
289 |
-
{"title": "λμ§νΈ μ ν", "style": "
|
290 |
{"title": "μ§μκ°λ₯μ±", "style": "Timeline Ribbon", "prompt_hint": "ESG μ λ΅"}
|
291 |
]
|
292 |
},
|
@@ -485,9 +497,17 @@ IMPORTANT Style Guidelines:
|
|
485 |
- Follow the user's instructions while maintaining this style
|
486 |
|
487 |
Emoji Guidelines:
|
488 |
-
- Professional emojis: π π― π‘ π β‘ π π π° π π§ π π β π¨ π± π€ π
|
489 |
- Match emoji to content meaning
|
490 |
-
- Use different emojis for variety
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
491 |
|
492 |
user_message = f"""Topic: {topic}
|
493 |
Slide Title: {slide_title}
|
@@ -523,37 +543,57 @@ Rewrite the content following the instruction above while maintaining concise, n
|
|
523 |
result = response.json()
|
524 |
content = result['choices'][0]['message']['content'].strip()
|
525 |
|
526 |
-
|
|
|
|
|
527 |
lines = content.split('\n')
|
528 |
subtitle = ""
|
529 |
bullet_points = []
|
530 |
|
531 |
for line in lines:
|
532 |
-
|
533 |
-
|
534 |
-
|
535 |
-
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
536 |
|
537 |
# νκΈλ‘ λ²μμ΄ νμν κ²½μ°
|
538 |
if any(ord('κ°') <= ord(char) <= ord('ν£') for char in topic):
|
539 |
subtitle = translate_content_to_korean(subtitle)
|
540 |
-
# λΆλ¦Ώ
|
541 |
translated_bullets = []
|
542 |
for point in bullet_points:
|
543 |
-
|
544 |
-
|
545 |
-
if len(
|
546 |
-
emoji =
|
547 |
-
text =
|
548 |
translated_text = translate_content_to_korean_concise(text)
|
549 |
translated_bullets.append(f"β’ {emoji} {translated_text}")
|
550 |
else:
|
551 |
-
translated_bullets.append(translate_content_to_korean_concise(
|
|
|
552 |
bullet_points = translated_bullets
|
553 |
|
554 |
return {
|
555 |
"subtitle": subtitle,
|
556 |
-
"bullet_points": bullet_points
|
557 |
}
|
558 |
else:
|
559 |
return {
|
@@ -731,13 +771,13 @@ Emoji Guidelines:
|
|
731 |
ποΈ (building/development), π± (sustainability/growth)
|
732 |
- Each bullet should have a different, relevant emoji
|
733 |
|
734 |
-
Output format:
|
735 |
Subtitle: [subtitle here]
|
736 |
-
|
737 |
-
|
738 |
-
|
739 |
-
|
740 |
-
|
741 |
|
742 |
user_message = f"""Topic: {topic}
|
743 |
Slide Title: {slide_title}
|
@@ -780,16 +820,41 @@ Context: {slide_context}"""
|
|
780 |
result = response.json()
|
781 |
content = result['choices'][0]['message']['content'].strip()
|
782 |
|
783 |
-
|
|
|
|
|
784 |
lines = content.split('\n')
|
785 |
subtitle = ""
|
786 |
bullet_points = []
|
787 |
|
788 |
for line in lines:
|
789 |
-
|
790 |
-
|
791 |
-
|
792 |
-
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
793 |
|
794 |
# νκΈλ‘ λ²μμ΄ νμν κ²½μ°
|
795 |
if any(ord('κ°') <= ord(char) <= ord('ν£') for char in topic):
|
@@ -797,22 +862,28 @@ Context: {slide_context}"""
|
|
797 |
# λΆλ¦Ώ ν¬μΈνΈλ μ΄λͺ¨μ§λ₯Ό μ μ§νλ©΄μ λ²μ
|
798 |
translated_bullets = []
|
799 |
for point in bullet_points:
|
800 |
-
#
|
801 |
-
|
802 |
-
|
803 |
-
|
804 |
-
|
|
|
|
|
|
|
805 |
translated_text = translate_content_to_korean_concise(text)
|
806 |
translated_bullets.append(f"β’ {emoji} {translated_text}")
|
807 |
else:
|
808 |
-
|
|
|
|
|
809 |
bullet_points = translated_bullets
|
810 |
|
811 |
return {
|
812 |
"subtitle": subtitle,
|
813 |
-
"bullet_points": bullet_points
|
814 |
}
|
815 |
else:
|
|
|
816 |
return {
|
817 |
"subtitle": slide_title,
|
818 |
"bullet_points": ["β’ π λ΄μ© μμ± λΆκ°"] * 5
|
@@ -1050,10 +1121,84 @@ def translate_to_english(text: str) -> str:
|
|
1050 |
return text
|
1051 |
|
1052 |
def generate_image(prompt: str, seed: int = 10, slide_info: str = "") -> Tuple[Image.Image, str]:
|
1053 |
-
"""Replicate APIλ₯Ό μ¬μ©ν΄ μ΄λ―Έμ§ μμ±"""
|
1054 |
print(f"\n[μ΄λ―Έμ§ μμ±] {slide_info}")
|
1055 |
print(f"[μ΄λ―Έμ§ μμ±] ν둬ννΈ: {prompt[:50]}...")
|
1056 |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
1057 |
try:
|
1058 |
english_prompt = translate_to_english(prompt)
|
1059 |
|
@@ -1995,6 +2140,10 @@ def generate_ppt_with_content(topic: str, template_name: str, custom_slides: Lis
|
|
1995 |
results = []
|
1996 |
preview_html = ""
|
1997 |
|
|
|
|
|
|
|
|
|
1998 |
# μ
λ‘λλ νμΌ λ΄μ© μ½κΈ°
|
1999 |
uploaded_content = ""
|
2000 |
if uploaded_file is not None:
|
@@ -2078,7 +2227,7 @@ def generate_ppt_with_content(topic: str, template_name: str, custom_slides: Lis
|
|
2078 |
slide_context, uploaded_content
|
2079 |
)
|
2080 |
|
2081 |
-
# μ΄λ―Έμ§ μμ±
|
2082 |
slide_seed = seed + i
|
2083 |
img, used_prompt = generate_image(prompt, slide_seed, slide_info)
|
2084 |
|
@@ -2154,9 +2303,8 @@ def generate_ppt_with_content(topic: str, template_name: str, custom_slides: Lis
|
|
2154 |
final_status = f"### π μμ± μλ£! μ΄ {total_slides}κ° μ¬λΌμ΄λ μ€ {successful}κ° μ±κ³΅"
|
2155 |
|
2156 |
# νΈμ§ κ°λ₯ν μ¬λΌμ΄λ λ°μ΄ν°λ₯Ό μ μ λ³μλ‘ μ μ₯
|
2157 |
-
global current_slides_data,
|
2158 |
current_slides_data = results
|
2159 |
-
current_topic = topic
|
2160 |
current_template = template_name
|
2161 |
current_theme = theme_name
|
2162 |
|
@@ -2164,6 +2312,8 @@ def generate_ppt_with_content(topic: str, template_name: str, custom_slides: Lis
|
|
2164 |
final_status += f"\n\n### π₯ PPTX νμΌμ΄ μ€λΉλμμ΅λλ€!"
|
2165 |
final_status += f"\n\nπ‘ **κ° μ¬λΌμ΄λλ₯Ό νΈμ§ν ν 'νΈμ§ μλ£ ν μ΅μ’
λ€μ΄λ‘λ' λ²νΌμ ν΄λ¦νμΈμ**"
|
2166 |
final_status += f"\n\nπ€ **λ°νμ λ
ΈνΈκ° κ° μ¬λΌμ΄λμ ν¬ν¨λμ΄ μμ΅λλ€!**"
|
|
|
|
|
2167 |
|
2168 |
yield preview_html, final_status, pptx_path
|
2169 |
|
@@ -2228,6 +2378,7 @@ with gr.Blocks(title="PPT μ΄λ―Έμ§ μμ±κΈ°", theme=gr.themes.Soft(), css="""
|
|
2228 |
- π **νμΌ μ
λ‘λ** μ§μ (PDF/CSV/TXT)
|
2229 |
- π **μΉ κ²μ** κΈ°λ₯ (Brave Search)
|
2230 |
- ποΈ **μ¬λΌμ΄λ μ μ‘°μ ** (6-20μ₯)
|
|
|
2231 |
""")
|
2232 |
|
2233 |
# API ν ν° μν νμΈ
|
@@ -2238,6 +2389,8 @@ with gr.Blocks(title="PPT μ΄λ―Έμ§ μμ±κΈ°", theme=gr.themes.Soft(), css="""
|
|
2238 |
token_status.append("β οΈ FRIENDLI_TOKEN νκ²½ λ³μκ° μ€μ λμ§ μμμ΅λλ€.")
|
2239 |
if not BRAVE_API_TOKEN:
|
2240 |
token_status.append("βΉοΈ BAPI_TOKENμ΄ μμ΄ μΉ κ²μ κΈ°λ₯μ΄ λΉνμ±νλ©λλ€.")
|
|
|
|
|
2241 |
|
2242 |
if token_status:
|
2243 |
gr.Markdown("\n".join(token_status))
|
@@ -2339,6 +2492,11 @@ with gr.Blocks(title="PPT μ΄λ―Έμ§ μμ±κΈ°", theme=gr.themes.Soft(), css="""
|
|
2339 |
- "μ«μ ν¬ν¨": ꡬ체μ μμΉλ ν΅κ³ μΆκ°
|
2340 |
- "μ λ¬Έμ μΌλ‘": μ
κ³ μ λ¬Έ μ©μ΄ μ¬μ©
|
2341 |
- "μν©νΈ μκ²": κ°μ‘°μ κ³Ό ν΅μ¬ μ±κ³Ό λΆκ°
|
|
|
|
|
|
|
|
|
|
|
2342 |
""")
|
2343 |
|
2344 |
# PPTX λ€μ΄λ‘λ μμ
|
@@ -2436,7 +2594,10 @@ with gr.Blocks(title="PPT μ΄λ―Έμ§ μμ±κΈ°", theme=gr.themes.Soft(), css="""
|
|
2436 |
info += f"μμ±λ μ¬λΌμ΄λ ({len(slides)}μ₯):\n"
|
2437 |
for i, slide in enumerate(slides):
|
2438 |
style_info = STYLE_TEMPLATES.get(slide['style'], {})
|
2439 |
-
info += f"{i+1}. {slide['title']} - {style_info.get('use_case', '')}
|
|
|
|
|
|
|
2440 |
|
2441 |
return info
|
2442 |
return ""
|
@@ -2483,72 +2644,59 @@ with gr.Blocks(title="PPT μ΄λ―Έμ§ μμ±κΈ°", theme=gr.themes.Soft(), css="""
|
|
2483 |
yield preview, status, pptx_file
|
2484 |
|
2485 |
# μ΄λ²€νΈ μ°κ²°
|
2486 |
-
# --- μ΄λ²€νΈ μ°κ²° -------------------------------------------------
|
2487 |
example_btn.click(
|
2488 |
fn=load_example,
|
2489 |
inputs=[template_select],
|
2490 |
outputs=[topic_input]
|
2491 |
)
|
2492 |
-
|
2493 |
theme_select.change(
|
2494 |
fn=update_theme_preview,
|
2495 |
inputs=[theme_select],
|
2496 |
outputs=[theme_preview]
|
2497 |
)
|
2498 |
-
|
2499 |
template_select.change(
|
2500 |
fn=update_template_info,
|
2501 |
inputs=[template_select, slide_count],
|
2502 |
outputs=[template_info]
|
2503 |
)
|
2504 |
-
|
2505 |
slide_count.change(
|
2506 |
fn=update_template_info,
|
2507 |
inputs=[template_select, slide_count],
|
2508 |
outputs=[template_info]
|
2509 |
)
|
2510 |
-
|
2511 |
custom_slide_count.change(
|
2512 |
fn=update_custom_slides_visibility,
|
2513 |
inputs=[custom_slide_count],
|
2514 |
-
outputs=[
|
2515 |
-
|
2516 |
-
for slide in custom_slides_components
|
2517 |
-
for comp in (slide["title"], slide["style"], slide["hint"])
|
2518 |
-
]
|
2519 |
)
|
2520 |
-
|
2521 |
# μ¬μ©μ μ μ μ¬λΌμ΄λ μ
λ ₯ μ»΄ν¬λνΈ ννν
|
2522 |
all_custom_inputs = []
|
2523 |
for slide in custom_slides_components:
|
2524 |
all_custom_inputs.extend([slide["title"], slide["style"], slide["hint"]])
|
2525 |
-
|
2526 |
generate_btn.click(
|
2527 |
fn=generate_ppt_handler,
|
2528 |
inputs=[
|
2529 |
-
topic_input,
|
2530 |
-
|
2531 |
-
|
2532 |
-
|
2533 |
-
seed_input,
|
2534 |
-
file_upload,
|
2535 |
-
use_web_search,
|
2536 |
-
custom_slide_count,
|
2537 |
-
]
|
2538 |
-
+ all_custom_inputs,
|
2539 |
-
outputs=[preview_output, status_output, download_file],
|
2540 |
)
|
2541 |
-
|
2542 |
# μ΄κΈ° λ‘λμ ν
νλ¦Ώ/ν
λ§ μ 보 νμ
|
2543 |
demo.load(
|
2544 |
-
fn=lambda: (
|
2545 |
-
|
2546 |
-
update_theme_preview(theme_select.value),
|
2547 |
-
),
|
2548 |
inputs=[],
|
2549 |
-
outputs=[template_info, theme_preview]
|
2550 |
)
|
2551 |
|
2552 |
-
#
|
2553 |
if __name__ == "__main__":
|
2554 |
-
demo.launch(ssr_mode=False)
|
|
|
19 |
import chardet
|
20 |
import gradio as gr
|
21 |
|
22 |
+
# νλ‘μΈμ€ νλ‘μ° μμ±κΈ° μν¬νΈ μΆκ°
|
23 |
+
try:
|
24 |
+
from process_flow_generator import generate_process_flow_for_ppt
|
25 |
+
PROCESS_FLOW_AVAILABLE = True
|
26 |
+
except ImportError:
|
27 |
+
PROCESS_FLOW_AVAILABLE = False
|
28 |
+
print("[κ²½κ³ ] process_flow_generatorλ₯Ό μν¬νΈν μ μμ΅λλ€. νλ‘μΈμ€ νλ‘μ° λ€μ΄μ΄κ·Έλ¨ κΈ°λ₯μ΄ λΉνμ±νλ©λλ€.")
|
29 |
+
|
30 |
# μμ ν
νλ¦Ώ μ μ
|
31 |
EXAMPLE_TOPICS = {
|
32 |
"λΉμ¦λμ€ μ μμ": "AI κΈ°λ° κ³ κ° μλΉμ€ μλν νλ«νΌ ν¬μ μ μ",
|
|
|
45 |
current_topic = ""
|
46 |
current_template = ""
|
47 |
current_theme = ""
|
48 |
+
uploaded_content = "" # μ μ λ³μλ‘ μΆκ°
|
49 |
|
50 |
# λμμΈ ν
λ§ μ μ
|
51 |
DESIGN_THEMES = {
|
|
|
116 |
}
|
117 |
}
|
118 |
|
119 |
+
# μ€νμΌ μ μ (νλ‘μΈμ€ νλ‘μ° μΆκ°)
|
120 |
STYLE_TEMPLATES = {
|
121 |
"Title Slide (Hero)": {
|
122 |
"name": "Title Slide",
|
|
|
152 |
"name": "Business Process",
|
153 |
"description": "End-to-end business workflow with clear phases",
|
154 |
"use_case": "νλ‘μΈμ€ μ€λͺ
, λ¨κ³λ³ μ§ν",
|
155 |
+
"example": "A detailed hand-drawn diagram illustrating an end-to-end business workflow with Market Analysis, Strategy Development, Product Design, Implementation, and Post-Launch Review phases. Clear directional arrows, iconography for each component, vibrant educational yet professional style",
|
156 |
+
"is_process_flow": True # νλ‘μΈμ€ νλ‘μ° λ€μ΄μ΄κ·Έλ¨ μ¬μ© νμ
|
157 |
},
|
158 |
"Industrial Design": {
|
159 |
"name": "Product Design",
|
|
|
213 |
"name": "Flowchart",
|
214 |
"description": "Vibrant flowchart with minimalistic icons",
|
215 |
"use_case": "μμ¬κ²°μ , νλ‘μΈμ€ νλ¦",
|
216 |
+
"example": "FLOWCHART DESIGN: A hand-drawn style flowchart, vibrant colors, minimalistic icons showing process flow from START to END with decision points, branches, and clear directional arrows",
|
217 |
+
"is_process_flow": True # νλ‘μΈμ€ νλ‘μ° λ€μ΄μ΄κ·Έλ¨ μ¬μ© νμ
|
218 |
}
|
219 |
}
|
220 |
|
221 |
# PPT ν
νλ¦Ώ μ μ (λμ μΌλ‘ μ¬λΌμ΄λ μ μ‘°μ κ°λ₯)
|
222 |
+
# PPT_TEMPLATES μμ - νλ‘μΈμ€ νλ‘μ°λ νΉμ μ¬λΌμ΄λμλ§ μ μ©
|
223 |
PPT_TEMPLATES = {
|
224 |
"λΉμ¦λμ€ μ μμ": {
|
225 |
"description": "ν¬μ μ μΉ, μ¬μ
μ μμ©",
|
226 |
"core_slides": [
|
227 |
+
{"title": "λͺ©μ°¨", "style": "Flowchart", "prompt_hint": "νλ μ ν
μ΄μ
ꡬ쑰"}, # μ΄κ±΄ λͺ©μ°¨μ© νλ‘μ°μ°¨νΈ
|
228 |
{"title": "λ¬Έμ μ μ", "style": "Colorful Mind Map", "prompt_hint": "νμ¬ μμ₯μ λ¬Έμ μ "},
|
229 |
{"title": "νν© λΆμ", "style": "Elegant SWOT Quadrant", "prompt_hint": "κ°μ , μ½μ , κΈ°ν, μν"},
|
230 |
{"title": "μ루μ
", "style": "Industrial Design", "prompt_hint": "μ ν/μλΉμ€ 컨μ
"},
|
231 |
+
{"title": "νλ‘μΈμ€", "style": "Business Workflow", "prompt_hint": "μ€ν λ¨κ³"}, # μ¬κΈ°μλ§ νλ‘μΈμ€ νλ‘μ°
|
232 |
{"title": "μΌμ ", "style": "Timeline Ribbon", "prompt_hint": "μ£Όμ λ§μΌμ€ν€"}
|
233 |
],
|
234 |
"optional_slides": [
|
|
|
238 |
{"title": "ν μκ°", "style": "Colorful Mind Map", "prompt_hint": "ν΅μ¬ νμκ³Ό μλ"},
|
239 |
{"title": "μ¬λ¬΄ κ³ν", "style": "KPI Dashboard", "prompt_hint": "μμ λ§€μΆκ³Ό μμ΅"},
|
240 |
{"title": "μν κ΄λ¦¬", "style": "Risk Heat Map", "prompt_hint": "μ£Όμ 리μ€ν¬μ λμ"},
|
241 |
+
{"title": "ννΈλμ", "style": "Value Chain", "prompt_hint": "μ λ΅μ μ ν΄"}, # Business Workflow λμ Value Chain
|
242 |
+
{"title": "κΈ°μ μ€ν", "style": "Industrial Design", "prompt_hint": "ν΅μ¬ κΈ°μ ꡬ쑰"}, # Flowchart λμ
|
243 |
{"title": "κ³ κ° μ¬λ‘", "style": "Industrial Design", "prompt_hint": "μ±κ³΅ μ¬λ‘"},
|
244 |
{"title": "μ±μ₯ μ λ΅", "style": "Timeline Ribbon", "prompt_hint": "νμ₯ κ³ν"},
|
245 |
{"title": "ν¬μ νμ©", "style": "Pyramid/Funnel", "prompt_hint": "μκΈ μ¬μ© κ³ν"},
|
|
|
252 |
{"title": "μ ν 컨μ
", "style": "Industrial Design", "prompt_hint": "μ ν λμμΈ"},
|
253 |
{"title": "μ¬μ©μ λμ¦", "style": "Colorful Mind Map", "prompt_hint": "κ³ κ° νμΈν¬μΈνΈ"},
|
254 |
{"title": "κΈ°λ₯ μκ°", "style": "Mobile App Mockup", "prompt_hint": "UI/UX νλ©΄"},
|
255 |
+
{"title": "μλ μ리", "style": "Flowchart", "prompt_hint": "κΈ°λ₯ νλ‘μ°"}, # μ¬κΈ°μλ§ νλ‘μΈμ€ νλ‘μ°
|
256 |
{"title": "μμ₯ ν¬μ§μ
", "style": "3D Bubble Chart", "prompt_hint": "κ²½μμ¬ λΉκ΅"},
|
257 |
{"title": "μΆμ μΌμ ", "style": "Timeline Ribbon", "prompt_hint": "λ°μΉ λ‘λλ§΅"}
|
258 |
],
|
|
|
260 |
{"title": "νκ² κ³ κ°", "style": "Colorful Mind Map", "prompt_hint": "μ£Όμ κ³ κ°μΈ΅"},
|
261 |
{"title": "κ°κ²© μ μ±
", "style": "Pyramid/Funnel", "prompt_hint": "κ°κ²© μ λ΅"},
|
262 |
{"title": "κΈ°μ μ°μ", "style": "Industrial Design", "prompt_hint": "ν΅μ¬ κΈ°μ "},
|
263 |
+
{"title": "μ¬μ© μλ리μ€", "style": "Mobile App Mockup", "prompt_hint": "νμ© μ¬λ‘"}, # Business Workflow λμ
|
264 |
{"title": "κ³ κ° νκΈ°", "style": "KPI Dashboard", "prompt_hint": "μ¬μ©μ νκ°"},
|
265 |
{"title": "νλ§€ μ±λ", "style": "Value Chain", "prompt_hint": "μ ν΅ μ λ΅"},
|
266 |
{"title": "λ§μΌν
μ λ΅", "style": "Timeline Ribbon", "prompt_hint": "ν보 κ³ν"},
|
|
|
270 |
"νλ‘μ νΈ λ³΄κ³ ": {
|
271 |
"description": "μ§ν μν©, μ±κ³Ό λ³΄κ³ μ©",
|
272 |
"core_slides": [
|
273 |
+
{"title": "νλ‘μ νΈ κ°μ", "style": "Business Workflow", "prompt_hint": "μ 체 νλ‘μΈμ€"}, # μ¬κΈ°μλ§
|
274 |
{"title": "μ§ν νν©", "style": "Gantt Chart", "prompt_hint": "μμ
μΌμ "},
|
275 |
{"title": "리μ€ν¬ κ΄λ¦¬", "style": "Risk Heat Map", "prompt_hint": "μν μμ"},
|
276 |
{"title": "μ±κ³Ό μ§ν", "style": "KPI Dashboard", "prompt_hint": "λ¬μ± μ€μ "},
|
|
|
281 |
{"title": "ν μ±κ³Ό", "style": "3D Bubble Chart", "prompt_hint": "νλ³ κΈ°μ¬λ"},
|
282 |
{"title": "μ΄μ κ΄λ¦¬", "style": "Risk Heat Map", "prompt_hint": "μ£Όμ μ΄μ"},
|
283 |
{"title": "κ°μ μ¬ν", "style": "Colorful Mind Map", "prompt_hint": "νλ‘μΈμ€ κ°μ "},
|
284 |
+
{"title": "κ΅ν", "style": "Colorful Mind Map", "prompt_hint": "λ°°μ΄ μ "} # Business Workflow λμ
|
285 |
]
|
286 |
},
|
287 |
"μ λ΅ κΈ°ν": {
|
|
|
297 |
"optional_slides": [
|
298 |
{"title": "μμ₯ μ λ§", "style": "3D Bubble Chart", "prompt_hint": "λ―Έλ μμ₯"},
|
299 |
{"title": "νμ λ°©ν₯", "style": "Industrial Design", "prompt_hint": "νμ μ λ΅"},
|
300 |
+
{"title": "μ‘°μ§ λ³ν", "style": "Value Chain", "prompt_hint": "μ‘°μ§ κ°νΈ"}, # Business Workflow λμ
|
301 |
+
{"title": "λμ§νΈ μ ν", "style": "Industrial Design", "prompt_hint": "DX μ λ΅"}, # Flowchart λμ
|
302 |
{"title": "μ§μκ°λ₯μ±", "style": "Timeline Ribbon", "prompt_hint": "ESG μ λ΅"}
|
303 |
]
|
304 |
},
|
|
|
497 |
- Follow the user's instructions while maintaining this style
|
498 |
|
499 |
Emoji Guidelines:
|
500 |
+
- Professional emojis: π π― π‘ π β‘ π π π° π π§ π π β π¨ π± π€ π ποΈ ποΈ π±
|
501 |
- Match emoji to content meaning
|
502 |
+
- Use different emojis for variety
|
503 |
+
|
504 |
+
Output format (EXACTLY FOLLOW THIS FORMAT):
|
505 |
+
Subtitle: [subtitle here]
|
506 |
+
- π― [Point 1]
|
507 |
+
- π [Point 2]
|
508 |
+
- π‘ [Point 3]
|
509 |
+
- π [Point 4]
|
510 |
+
- β‘ [Point 5]"""
|
511 |
|
512 |
user_message = f"""Topic: {topic}
|
513 |
Slide Title: {slide_title}
|
|
|
543 |
result = response.json()
|
544 |
content = result['choices'][0]['message']['content'].strip()
|
545 |
|
546 |
+
print(f"[AI μ¬μμ±] LLM μλ΅:\n{content}")
|
547 |
+
|
548 |
+
# Parse content - λ κ°κ±΄ν νμ±
|
549 |
lines = content.split('\n')
|
550 |
subtitle = ""
|
551 |
bullet_points = []
|
552 |
|
553 |
for line in lines:
|
554 |
+
line = line.strip()
|
555 |
+
if not line:
|
556 |
+
continue
|
557 |
+
|
558 |
+
# Subtitle νμ±
|
559 |
+
if line.lower().startswith("subtitle:") or line.startswith("Subtitle:"):
|
560 |
+
subtitle = line.split(':', 1)[1].strip()
|
561 |
+
# Bullet point νμ±
|
562 |
+
elif line.startswith("β’") or line.startswith("-") or (len(line) > 2 and line[1] == ' ' and ord(line[0]) >= 128):
|
563 |
+
if not line.startswith("β’"):
|
564 |
+
line = "β’ " + line.lstrip("- ")
|
565 |
+
bullet_points.append(line)
|
566 |
+
|
567 |
+
# κΈ°λ³Έκ° μ²λ¦¬
|
568 |
+
if not subtitle:
|
569 |
+
subtitle = f"{slide_title} κ°μ"
|
570 |
+
|
571 |
+
while len(bullet_points) < 5:
|
572 |
+
bullet_points.append(f"β’ π ν¬μΈνΈ {len(bullet_points) + 1}")
|
573 |
+
|
574 |
+
bullet_points = bullet_points[:5]
|
575 |
|
576 |
# νκΈλ‘ λ²μμ΄ νμν κ²½μ°
|
577 |
if any(ord('κ°') <= ord(char) <= ord('ν£') for char in topic):
|
578 |
subtitle = translate_content_to_korean(subtitle)
|
579 |
+
# λΆλ¦Ώ ν¬μΈνΈ λ²μ
|
580 |
translated_bullets = []
|
581 |
for point in bullet_points:
|
582 |
+
clean_point = point.replace('β’', '').strip()
|
583 |
+
|
584 |
+
if len(clean_point) > 0 and clean_point[0] in 'π―ππ‘πβ‘πππ°ππ§ππβπ¨π±π€πποΈποΈπ±':
|
585 |
+
emoji = clean_point[0]
|
586 |
+
text = clean_point[1:].strip()
|
587 |
translated_text = translate_content_to_korean_concise(text)
|
588 |
translated_bullets.append(f"β’ {emoji} {translated_text}")
|
589 |
else:
|
590 |
+
translated_bullets.append(f"β’ {translate_content_to_korean_concise(clean_point)}")
|
591 |
+
|
592 |
bullet_points = translated_bullets
|
593 |
|
594 |
return {
|
595 |
"subtitle": subtitle,
|
596 |
+
"bullet_points": bullet_points
|
597 |
}
|
598 |
else:
|
599 |
return {
|
|
|
771 |
ποΈ (building/development), π± (sustainability/growth)
|
772 |
- Each bullet should have a different, relevant emoji
|
773 |
|
774 |
+
Output format (EXACTLY FOLLOW THIS FORMAT):
|
775 |
Subtitle: [subtitle here]
|
776 |
+
- π― [Point 1 - noun ending or fragment]
|
777 |
+
- π [Point 2 - noun ending or fragment]
|
778 |
+
- π‘ [Point 3 - noun ending or fragment]
|
779 |
+
- π [Point 4 - noun ending or fragment]
|
780 |
+
- β‘ [Point 5 - noun ending or fragment]"""
|
781 |
|
782 |
user_message = f"""Topic: {topic}
|
783 |
Slide Title: {slide_title}
|
|
|
820 |
result = response.json()
|
821 |
content = result['choices'][0]['message']['content'].strip()
|
822 |
|
823 |
+
print(f"[μ¬λΌμ΄λ λ΄μ©] LLM μλ΅:\n{content}")
|
824 |
+
|
825 |
+
# Parse content - λ κ°κ±΄ν νμ±
|
826 |
lines = content.split('\n')
|
827 |
subtitle = ""
|
828 |
bullet_points = []
|
829 |
|
830 |
for line in lines:
|
831 |
+
line = line.strip()
|
832 |
+
if not line:
|
833 |
+
continue
|
834 |
+
|
835 |
+
# Subtitle νμ±
|
836 |
+
if line.lower().startswith("subtitle:") or line.startswith("Subtitle:"):
|
837 |
+
subtitle = line.split(':', 1)[1].strip()
|
838 |
+
# Bullet point νμ± - β’ λλ - λ‘ μμνλ κ²½μ° λͺ¨λ μ²λ¦¬
|
839 |
+
elif line.startswith("β’") or line.startswith("-") or (len(line) > 2 and line[1] == ' ' and ord(line[0]) >= 128):
|
840 |
+
# μ΄λ―Έ β’ κ° μμΌλ©΄ κ·Έλλ‘, μμΌλ©΄ μΆκ°
|
841 |
+
if not line.startswith("β’"):
|
842 |
+
line = "β’ " + line.lstrip("- ")
|
843 |
+
bullet_points.append(line)
|
844 |
+
|
845 |
+
# λ§μ½ subtitleμ΄ μμΌλ©΄ κΈ°λ³Έκ° μ¬μ©
|
846 |
+
if not subtitle:
|
847 |
+
subtitle = f"{slide_title} κ°μ"
|
848 |
+
|
849 |
+
# λ§μ½ bullet_pointsκ° 5κ° λ―Έλ§μ΄λ©΄ κΈ°λ³Έκ° μΆκ°
|
850 |
+
while len(bullet_points) < 5:
|
851 |
+
bullet_points.append(f"β’ π ν¬μΈνΈ {len(bullet_points) + 1}")
|
852 |
+
|
853 |
+
# 5κ°λ§ μ ν
|
854 |
+
bullet_points = bullet_points[:5]
|
855 |
+
|
856 |
+
print(f"[μ¬λΌμ΄λ λ΄μ©] νμ±λ subtitle: {subtitle}")
|
857 |
+
print(f"[μ¬λΌμ΄λ λ΄μ©] νμ±λ bullets: {bullet_points}")
|
858 |
|
859 |
# νκΈλ‘ λ²μμ΄ νμν κ²½μ°
|
860 |
if any(ord('κ°') <= ord(char) <= ord('ν£') for char in topic):
|
|
|
862 |
# λΆλ¦Ώ ν¬μΈνΈλ μ΄λͺ¨μ§λ₯Ό μ μ§νλ©΄μ λ²μ
|
863 |
translated_bullets = []
|
864 |
for point in bullet_points:
|
865 |
+
# β’ μ κ±°νκ³ λ΄μ©λ§ μΆμΆ
|
866 |
+
clean_point = point.replace('β’', '').strip()
|
867 |
+
|
868 |
+
# μ΄λͺ¨μ§μ ν
μ€νΈ λΆλ¦¬ (첫 λ²μ§Έ 곡백μ κΈ°μ€μΌλ‘)
|
869 |
+
if len(clean_point) > 0 and clean_point[0] in 'π―ππ‘πβ‘πππ°ππ§ππβπ¨π±π€πποΈποΈπ±':
|
870 |
+
# μ΄λͺ¨μ§κ° μλ κ²½μ°
|
871 |
+
emoji = clean_point[0]
|
872 |
+
text = clean_point[1:].strip()
|
873 |
translated_text = translate_content_to_korean_concise(text)
|
874 |
translated_bullets.append(f"β’ {emoji} {translated_text}")
|
875 |
else:
|
876 |
+
# μ΄λͺ¨μ§κ° μλ κ²½μ°
|
877 |
+
translated_bullets.append(f"β’ {translate_content_to_korean_concise(clean_point)}")
|
878 |
+
|
879 |
bullet_points = translated_bullets
|
880 |
|
881 |
return {
|
882 |
"subtitle": subtitle,
|
883 |
+
"bullet_points": bullet_points
|
884 |
}
|
885 |
else:
|
886 |
+
print(f"[μ¬λΌμ΄λ λ΄μ©] API μ€λ₯: {response.status_code}")
|
887 |
return {
|
888 |
"subtitle": slide_title,
|
889 |
"bullet_points": ["β’ π λ΄μ© μμ± λΆκ°"] * 5
|
|
|
1121 |
return text
|
1122 |
|
1123 |
def generate_image(prompt: str, seed: int = 10, slide_info: str = "") -> Tuple[Image.Image, str]:
|
1124 |
+
"""Replicate APIλ₯Ό μ¬μ©ν΄ μ΄λ―Έμ§ μμ± λλ νλ‘μΈμ€ νλ‘μ° λ€μ΄μ΄κ·Έλ¨ μμ±"""
|
1125 |
print(f"\n[μ΄λ―Έμ§ μμ±] {slide_info}")
|
1126 |
print(f"[μ΄λ―Έμ§ μμ±] ν둬ννΈ: {prompt[:50]}...")
|
1127 |
|
1128 |
+
global current_topic, uploaded_content
|
1129 |
+
|
1130 |
+
# νλ‘μΈμ€ νλ‘μ° λ€μ΄μ΄κ·Έλ¨ μμ± μ‘°κ±΄ νμΈ
|
1131 |
+
should_generate_process_flow = False
|
1132 |
+
|
1133 |
+
# μ¬λΌμ΄λ μ 보μμ μ λͺ© μΆμΆ
|
1134 |
+
slide_title = ""
|
1135 |
+
if ":" in slide_info:
|
1136 |
+
# "μ¬λΌμ΄λ 5: νλ‘μΈμ€" ννμμ "νλ‘μΈμ€" μΆμΆ
|
1137 |
+
parts = slide_info.split(":")
|
1138 |
+
if len(parts) >= 2:
|
1139 |
+
slide_title = parts[1].strip()
|
1140 |
+
|
1141 |
+
print(f"[μ΄λ―Έμ§ μμ±] μΆμΆλ μ¬λΌμ΄λ μ λͺ©: '{slide_title}'")
|
1142 |
+
|
1143 |
+
# 1. νλ‘μΈμ€ κ΄λ ¨ ν€μλκ° μ¬λΌμ΄λ μ λͺ©μ ν¬ν¨λμ΄ μλμ§ νμΈ
|
1144 |
+
process_keywords = [
|
1145 |
+
"νλ‘μΈμ€", "μλ", "νλ‘μ°", "νλ¦", "μν¬νλ‘μ°",
|
1146 |
+
"μ μ°¨", "λ¨κ³", "μ²λ¦¬", "μ§ν", "κ°μ"
|
1147 |
+
]
|
1148 |
+
|
1149 |
+
# 2. μ€νμΌ νμΈ
|
1150 |
+
is_workflow_style = False
|
1151 |
+
if any(style in prompt for style in ["Business Workflow", "Flowchart", "Business Process"]):
|
1152 |
+
is_workflow_style = True
|
1153 |
+
print(f"[μ΄λ―Έμ§ μμ±] Business Workflow λλ Flowchart μ€νμΌ κ°μ§")
|
1154 |
+
|
1155 |
+
# 3. μ¬λΌμ΄λ μ λͺ©μ νλ‘μΈμ€ ν€μλκ° μκ±°λ, μ€νμΌμ΄ μν¬νλ‘μ°μΈ κ²½μ°
|
1156 |
+
title_has_process = any(keyword in slide_title for keyword in process_keywords)
|
1157 |
+
prompt_has_process = any(keyword in prompt.lower() for keyword in ["process", "flow", "workflow"])
|
1158 |
+
|
1159 |
+
print(f"[μ΄λ―Έμ§ μμ±] μ λͺ©μ νλ‘μΈμ€ ν€μλ: {title_has_process}")
|
1160 |
+
print(f"[μ΄λ―Έμ§ μμ±] μν¬νλ‘μ° μ€νμΌ: {is_workflow_style}")
|
1161 |
+
print(f"[μ΄λ―Έμ§ μμ±] ν둬ννΈμ νλ‘μΈμ€ ν€μλ: {prompt_has_process}")
|
1162 |
+
|
1163 |
+
# 쑰건 μν: μ λͺ©μ νλ‘μΈμ€ ν€μλκ° οΏ½οΏ½κ³ , μ€νμΌμ΄ λ§κ±°λ ν둬ννΈμ κ΄λ ¨ ν€μλκ° μμΌλ©΄ μμ±
|
1164 |
+
if title_has_process and (is_workflow_style or prompt_has_process):
|
1165 |
+
should_generate_process_flow = True
|
1166 |
+
print(f"[μ΄λ―Έμ§ μμ±] β
νλ‘μΈμ€ νλ‘μ° λ€μ΄μ΄κ·Έλ¨ μμ± μ‘°κ±΄ μΆ©μ‘±!")
|
1167 |
+
|
1168 |
+
# νΉλ³ μΌμ΄μ€: "λͺ©μ°¨"λ νλ‘μΈμ€ νλ‘μ°κ° μλ μΌλ° μ΄λ―Έμ§λ‘
|
1169 |
+
if "λͺ©μ°¨" in slide_title:
|
1170 |
+
should_generate_process_flow = False
|
1171 |
+
print(f"[μ΄λ―Έμ§ μμ±] λͺ©μ°¨λ νλ‘μΈμ€ νλ‘μ° μ μΈ")
|
1172 |
+
|
1173 |
+
# νλ‘μΈμ€ νλ‘μ° λ€μ΄μ΄κ·Έλ¨ μμ±
|
1174 |
+
if PROCESS_FLOW_AVAILABLE and should_generate_process_flow:
|
1175 |
+
try:
|
1176 |
+
print("[μ΄λ―Έμ§ μμ±] π§ νλ‘μΈμ€ νλ‘μ° λ€μ΄μ΄κ·Έλ¨ μμ± μμ...")
|
1177 |
+
|
1178 |
+
# μ¬λΌμ΄λ 컨ν
μ€νΈλ₯Ό κΈ°λ°μΌλ‘ μ μ ν νλ‘μΈμ€ νλ‘μ° μμ±
|
1179 |
+
img = generate_process_flow_for_ppt(
|
1180 |
+
topic=current_topic,
|
1181 |
+
context=slide_info,
|
1182 |
+
style="Business Workflow"
|
1183 |
+
)
|
1184 |
+
|
1185 |
+
if isinstance(img, Image.Image):
|
1186 |
+
print("[μ΄λ―Έμ§ μμ±] β
νλ‘μΈμ€ νλ‘μ° λ€μ΄μ΄κ·Έλ¨ μμ± μ±κ³΅!")
|
1187 |
+
return img, "Process flow diagram generated with Korean support"
|
1188 |
+
else:
|
1189 |
+
print("[μ΄λ―Έμ§ μμ±] β νλ‘μΈμ€ νλ‘μ° μμ± μ€ν¨, μΌλ° μ΄λ―Έμ§λ‘ λ체")
|
1190 |
+
except Exception as e:
|
1191 |
+
print(f"[μ΄λ―Έμ§ μμ±] β νλ‘μΈμ€ νλ‘μ° μμ± μ€λ₯: {str(e)}")
|
1192 |
+
import traceback
|
1193 |
+
traceback.print_exc()
|
1194 |
+
# μ€ν¨μ μΌλ° μ΄λ―Έμ§ μμ±μΌλ‘ ν΄λ°±
|
1195 |
+
else:
|
1196 |
+
if not PROCESS_FLOW_AVAILABLE:
|
1197 |
+
print("[μ΄λ―Έμ§ μμ±] β οΈ νλ‘μΈμ€ νλ‘μ° μμ±κΈ°λ₯Ό μ¬μ©ν μ μμ")
|
1198 |
+
else:
|
1199 |
+
print("[μ΄λ―Έμ§ μμ±] βΉοΈ νλ‘μΈμ€ νλ‘μ° μμ± μ‘°κ±΄ λ―ΈμΆ©μ‘±, μΌλ° μ΄λ―Έμ§ μμ±")
|
1200 |
+
|
1201 |
+
# κΈ°μ‘΄ μ΄λ―Έμ§ μμ± λ‘μ§
|
1202 |
try:
|
1203 |
english_prompt = translate_to_english(prompt)
|
1204 |
|
|
|
2140 |
results = []
|
2141 |
preview_html = ""
|
2142 |
|
2143 |
+
# μ μ λ³μ μ
λ°μ΄νΈ
|
2144 |
+
global current_topic, uploaded_content
|
2145 |
+
current_topic = topic
|
2146 |
+
|
2147 |
# μ
λ‘λλ νμΌ λ΄μ© μ½κΈ°
|
2148 |
uploaded_content = ""
|
2149 |
if uploaded_file is not None:
|
|
|
2227 |
slide_context, uploaded_content
|
2228 |
)
|
2229 |
|
2230 |
+
# μ΄λ―Έμ§ μμ± (νλ‘μΈμ€ νλ‘μ° λ€μ΄μ΄κ·Έλ¨ μ§μ ν¬ν¨)
|
2231 |
slide_seed = seed + i
|
2232 |
img, used_prompt = generate_image(prompt, slide_seed, slide_info)
|
2233 |
|
|
|
2303 |
final_status = f"### π μμ± μλ£! μ΄ {total_slides}κ° μ¬λΌμ΄λ μ€ {successful}κ° μ±κ³΅"
|
2304 |
|
2305 |
# νΈμ§ κ°λ₯ν μ¬λΌμ΄λ λ°μ΄ν°λ₯Ό μ μ λ³μλ‘ μ μ₯
|
2306 |
+
global current_slides_data, current_template, current_theme
|
2307 |
current_slides_data = results
|
|
|
2308 |
current_template = template_name
|
2309 |
current_theme = theme_name
|
2310 |
|
|
|
2312 |
final_status += f"\n\n### π₯ PPTX νμΌμ΄ μ€λΉλμμ΅λλ€!"
|
2313 |
final_status += f"\n\nπ‘ **κ° μ¬λΌμ΄λλ₯Ό νΈμ§ν ν 'νΈμ§ μλ£ ν μ΅μ’
λ€μ΄λ‘λ' λ²νΌμ ν΄λ¦νμΈμ**"
|
2314 |
final_status += f"\n\nπ€ **λ°νμ λ
ΈνΈκ° κ° μ¬λΌμ΄λμ ν¬ν¨λμ΄ μμ΅λλ€!**"
|
2315 |
+
if PROCESS_FLOW_AVAILABLE:
|
2316 |
+
final_status += f"\n\nπ§ **νλ‘μΈμ€ νλ‘μ° λ€μ΄μ΄κ·Έλ¨μ΄ μλμΌλ‘ μμ±λ©λλ€ (νκΈ μ§μ)**"
|
2317 |
|
2318 |
yield preview_html, final_status, pptx_path
|
2319 |
|
|
|
2378 |
- π **νμΌ μ
λ‘λ** μ§μ (PDF/CSV/TXT)
|
2379 |
- π **μΉ κ²μ** κΈ°λ₯ (Brave Search)
|
2380 |
- ποΈ **μ¬λΌμ΄λ μ μ‘°μ ** (6-20μ₯)
|
2381 |
+
- π§ **νλ‘μΈμ€ νλ‘μ° λ€μ΄μ΄κ·Έλ¨** μλ μμ± (νκΈ μ§μ)
|
2382 |
""")
|
2383 |
|
2384 |
# API ν ν° μν νμΈ
|
|
|
2389 |
token_status.append("β οΈ FRIENDLI_TOKEN νκ²½ λ³μκ° μ€μ λμ§ μμμ΅λλ€.")
|
2390 |
if not BRAVE_API_TOKEN:
|
2391 |
token_status.append("βΉοΈ BAPI_TOKENμ΄ μμ΄ μΉ κ²μ κΈ°λ₯μ΄ λΉνμ±νλ©λλ€.")
|
2392 |
+
if not PROCESS_FLOW_AVAILABLE:
|
2393 |
+
token_status.append("βΉοΈ process_flow_generatorκ° μμ΄ νλ‘μΈμ€ νλ‘μ° λ€μ΄μ΄κ·Έλ¨μ΄ λΉνμ±νλ©λλ€.")
|
2394 |
|
2395 |
if token_status:
|
2396 |
gr.Markdown("\n".join(token_status))
|
|
|
2492 |
- "μ«μ ν¬ν¨": ꡬ체μ μμΉλ ν΅κ³ μΆκ°
|
2493 |
- "μ λ¬Έμ μΌλ‘": μ
κ³ μ λ¬Έ μ©μ΄ μ¬μ©
|
2494 |
- "μν©νΈ μκ²": κ°μ‘°μ κ³Ό ν΅μ¬ μ±κ³Ό λΆκ°
|
2495 |
+
|
2496 |
+
### π§ νλ‘μΈμ€ νλ‘μ° λ€μ΄μ΄κ·Έλ¨:
|
2497 |
+
- **Business Workflow** λλ **Flowchart** μ€νμΌ μ ν μ μλμΌλ‘ νλ‘μΈμ€ νλ‘μ° λ€μ΄μ΄κ·Έλ¨ μμ±
|
2498 |
+
- νκΈ ν
μ€νΈλ₯Ό μλ²½νκ² μ§μνλ λ€μ΄μ΄κ·Έλ¨
|
2499 |
+
- νλ‘μΈμ€, μμ¬κ²°μ , μ
μΆλ ₯ λ± λ€μν λ
Έλ νμ
μ§μ
|
2500 |
""")
|
2501 |
|
2502 |
# PPTX λ€μ΄λ‘λ μμ
|
|
|
2594 |
info += f"μμ±λ μ¬λΌμ΄λ ({len(slides)}μ₯):\n"
|
2595 |
for i, slide in enumerate(slides):
|
2596 |
style_info = STYLE_TEMPLATES.get(slide['style'], {})
|
2597 |
+
info += f"{i+1}. {slide['title']} - {style_info.get('use_case', '')}"
|
2598 |
+
if style_info.get('is_process_flow'):
|
2599 |
+
info += " π§"
|
2600 |
+
info += "\n"
|
2601 |
|
2602 |
return info
|
2603 |
return ""
|
|
|
2644 |
yield preview, status, pptx_file
|
2645 |
|
2646 |
# μ΄λ²€νΈ μ°κ²°
|
|
|
2647 |
example_btn.click(
|
2648 |
fn=load_example,
|
2649 |
inputs=[template_select],
|
2650 |
outputs=[topic_input]
|
2651 |
)
|
2652 |
+
|
2653 |
theme_select.change(
|
2654 |
fn=update_theme_preview,
|
2655 |
inputs=[theme_select],
|
2656 |
outputs=[theme_preview]
|
2657 |
)
|
2658 |
+
|
2659 |
template_select.change(
|
2660 |
fn=update_template_info,
|
2661 |
inputs=[template_select, slide_count],
|
2662 |
outputs=[template_info]
|
2663 |
)
|
2664 |
+
|
2665 |
slide_count.change(
|
2666 |
fn=update_template_info,
|
2667 |
inputs=[template_select, slide_count],
|
2668 |
outputs=[template_info]
|
2669 |
)
|
2670 |
+
|
2671 |
custom_slide_count.change(
|
2672 |
fn=update_custom_slides_visibility,
|
2673 |
inputs=[custom_slide_count],
|
2674 |
+
outputs=[comp for slide in custom_slides_components
|
2675 |
+
for comp in [slide["title"], slide["style"], slide["hint"]]]
|
|
|
|
|
|
|
2676 |
)
|
2677 |
+
|
2678 |
# μ¬μ©μ μ μ μ¬λΌμ΄λ μ
λ ₯ μ»΄ν¬λνΈ ννν
|
2679 |
all_custom_inputs = []
|
2680 |
for slide in custom_slides_components:
|
2681 |
all_custom_inputs.extend([slide["title"], slide["style"], slide["hint"]])
|
2682 |
+
|
2683 |
generate_btn.click(
|
2684 |
fn=generate_ppt_handler,
|
2685 |
inputs=[
|
2686 |
+
topic_input, template_select, theme_select, slide_count,
|
2687 |
+
seed_input, file_upload, use_web_search, custom_slide_count
|
2688 |
+
] + all_custom_inputs,
|
2689 |
+
outputs=[preview_output, status_output, download_file]
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
2690 |
)
|
2691 |
+
|
2692 |
# μ΄κΈ° λ‘λμ ν
νλ¦Ώ/ν
λ§ μ 보 νμ
|
2693 |
demo.load(
|
2694 |
+
fn=lambda: (update_template_info(template_select.value, slide_count.value),
|
2695 |
+
update_theme_preview(theme_select.value)),
|
|
|
|
|
2696 |
inputs=[],
|
2697 |
+
outputs=[template_info, theme_preview]
|
2698 |
)
|
2699 |
|
2700 |
+
# μ± μ€ν
|
2701 |
if __name__ == "__main__":
|
2702 |
+
demo.launch(ssr_mode=False)
|