openfree commited on
Commit
05ba644
ยท
verified ยท
1 Parent(s): 0710314

Update app.py

Browse files
Files changed (1) hide show
  1. app.py +168 -75
app.py CHANGED
@@ -623,97 +623,190 @@ def generate_diagram_locally(json_data: str, diagram_type: str, output_format: s
623
  ##############################################################################
624
  # โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€ Prompt Templates (6 Styles) โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€
625
  EXAMPLE_PROMPTS: dict[str, str] = {
626
- "Product Design": """A sleek industrial design concept for a coffee machine:
627
- - Curved metallic body with minimal bezel
628
- - Touchscreen panel for settings
629
- - Modern matte black finish
630
- - Hand-drawn concept sketch style""",
631
- "Mindmap": """A handrawn colorful mind map diagram, educational style, vibrant colors, clear hierarchy, golden ratio layout.
632
- KNOWLEDGE
633
- โ”œโ”€โ”€ ACQUISITION [Brain with Lightning ~60px]
634
- โ”‚ โ”œโ”€โ”€ READING [Open Book with Glow]
635
- โ”‚ โ”œโ”€โ”€ PRACTICE [Hands-on Tools]
636
- โ”‚ โ””โ”€โ”€ OBSERVATION [Eye with Magnifier]
637
- โ”œโ”€โ”€ PROCESSING [Gear Network ~50px]
638
- โ”‚ โ”œโ”€โ”€ ANALYSIS [Graph Trending Up]
639
- โ”‚ โ””โ”€โ”€ SYNTHESIS [Puzzle Pieces]
640
- โ”œโ”€โ”€ RETENTION [Memory Chip ~45px]
641
- โ”‚ โ”œโ”€โ”€ SHORT-TERM [Quick Flash]
642
- โ”‚ โ””โ”€โ”€ LONG-TERM [Solid Archive]
643
- โ””โ”€โ”€ APPLICATION
644
- โ”œโ”€โ”€ CREATION [Artist Palette]
645
- โ””โ”€โ”€ INNOVATION [Lightbulb Constellation]""",
646
- "Mockup": """A clean hand-drawn style wireframe for a mobile banking app:
647
- - Title screen with logo
648
- - Login screen (username, password, login button)
649
- - Dashboard with 3 main sections (balance, transactions, quick actions)
650
- - Bottom navigation bar (home, transfers, profile)""",
651
- "Infographic": """A sophisticated flat-style infographic for a multinational corporation's annual report:
652
- - Title: "Global Renewable Energy Trends 2025"
653
- - Subtitle: "Market Share and Growth Analysis"
654
- - Visual Elements:
655
- - Multi-segmented bar charts comparing Solar, Wind, and Hydro energy production across regions
656
- - Pie chart displaying overall energy distribution: Solar (45%), Wind (30%), Hydro (25%)
657
- - Trend lines indicating year-over-year growth
658
- - Icons: Sleek, minimalist representations of a sun, wind turbine, and water droplet
659
- - Layout: Clean, grid-based design with ample white space and pastel accents for a modern corporate look
660
- - Annotations: Brief, impactful data callouts highlighting key performance indicators and future forecasts""",
661
- "Diagram": """A detailed hand-drawn diagram illustrating an end-to-end business workflow:
662
- - Title: "Integrated Business Process Diagram"
663
- - Components:
664
- - Market Analysis โ†’ Strategy Development โ†’ Product Design โ†’ Implementation โ†’ Post-Launch Review
665
- - Visual Elements:
666
- - Directional arrows, magnifying glass, lightbulb, gear, checklist icons
667
- - Style: Vibrant, educational yet professional
668
- - Layout: Clear hierarchy, color-coded sections""",
669
- "Flowchart": """A hand-drawn style flowchart, vibrant colors, minimalistic icons.
670
- BUSINESS WORKFLOW
671
- โ”œโ”€โ”€ START [Green Button ~40px]
672
- โ”‚ โ”œโ”€โ”€ COLLECT REQUIREMENTS [Folder Icon]
673
- โ”‚ โ””โ”€โ”€ ANALYZE DATA [Chart Icon]
674
- โ”œโ”€โ”€ IMPLEMENTATION [Coding Symbol ~50px]
675
- โ”‚ โ”œโ”€โ”€ FRONTEND [Browser Icon]
676
- โ”‚ โ””โ”€โ”€ BACKEND [Server Icon]
677
- โ”œโ”€โ”€ TEST & INTEGRATION [Gear Icon ~45px]
678
- โ””โ”€โ”€ DEPLOY โ†’ END [Checkered Flag ~40px]"""
679
  }
680
  STYLE_KEYS = list(EXAMPLE_PROMPTS.keys())
681
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
682
  def generate_flux_prompt(title: str, content: str, style_key: str) -> str:
683
  """
684
- Build a FLUX image-generation prompt for one slide, following six
685
- pre-defined visual styles (Product Design, Mindmap, Mockup, Infographic,
686
- Diagram, Flowchart). `content` is the raw bullet-point block coming from
687
- the slide; `style_key` picks which template to use.
688
  """
689
- # 1) Extract clean bullet points (max 8)
690
  bullets = [
691
- re.sub(r'^[\-\โ€ข\โ—]\s*', '', line).strip()
692
  for line in content.splitlines()
693
- if line.strip().startswith(('-', 'โ€ข', 'โ—'))
694
  ][:8]
695
 
696
- # Build node / list representation for templates that need it
697
  if bullets:
698
- node_block = '\n'.join([f"- {b}" for b in bullets])
699
- tree_block = '\n'.join([f"โ”œโ”€โ”€ {b}" if i < len(bullets) - 1 else f"โ””โ”€โ”€ {b}"
700
- for i, b in enumerate(bullets)])
 
 
701
  else:
702
- node_block = "- No explicit bullet points provided -"
703
- tree_block = node_block
704
 
705
- # 2) Select template
706
- tpl = EXAMPLE_PROMPTS.get(style_key, EXAMPLE_PROMPTS["Diagram"])
 
707
 
708
- # 3) Inject dynamic parts
709
- prompt_body = tpl.format(title=title, nodes=node_block if "{nodes}" in tpl else tree_block)
 
 
 
710
 
711
- # 4) Final stylistic tail โ€“ ensure English, โ‰ค120 words
712
- style_tail = "Corporate palette, white background, high-resolution vector, clean composition."
713
 
714
- # 5) Return full prompt (no markdown / commentary)
715
- full_prompt = f"{prompt_body}\n\n{style_tail}"
716
- return full_prompt.strip()
717
 
718
 
719
  def generate_flux_image_via_api(prompt: str) -> Optional[str]:
 
623
  ##############################################################################
624
  # โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€ Prompt Templates (6 Styles) โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€
625
  EXAMPLE_PROMPTS: dict[str, str] = {
626
+
627
+ "Product Design": (
628
+ "A sleek industrial product-design sketch.\n"
629
+ "{nodes}"
630
+ ),
631
+ "Mindmap": (
632
+ "A hand-drawn colorful mind-map, educational style, clear hierarchy.\n"
633
+ "{nodes}"
634
+ ),
635
+ "Mockup": (
636
+ "A clean hand-drawn wire-frame for a mobile banking app.\n"
637
+ "{nodes}"
638
+ ),
639
+ "Infographic": (
640
+ "A flat corporate infographic โ€“ โ€œGlobal Renewable Energy Trends 2025โ€.\n"
641
+ "{nodes}"
642
+ ),
643
+ "Diagram": (
644
+ "A hand-drawn business process diagram.\n"
645
+ "{nodes}"
646
+ ),
647
+ "Flowchart": (
648
+ "A vibrant hand-drawn flow-chart.\n"
649
+ "{nodes}"
650
+ ),
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
651
  }
652
  STYLE_KEYS = list(EXAMPLE_PROMPTS.keys())
653
 
654
+ def create_advanced_ppt_from_content(
655
+ slides_data: list,
656
+ topic: str,
657
+ theme_name: str = "professional",
658
+ include_charts: bool = False,
659
+ include_ai_image: bool = False,
660
+ include_diagrams: bool = False,
661
+ include_flux_images: bool = False,
662
+ max_images_per_api: int = 3,
663
+ max_diagrams: int = 2,
664
+ ) -> str:
665
+ """
666
+ Build a fully-styled 16:9 PowerPoint file from parsed slide data.
667
+
668
+ โ€ข ์Šฌ๋ผ์ด๋“œ ํ…Œ๋งˆ/ํฐํŠธ/์ƒ‰์ƒ์€ DESIGN_THEMES์—์„œ ๋Œ์–ด์˜จ๋‹ค.
669
+ โ€ข AI ์ด๋ฏธ์ง€: 3D-API์™€ FLUX-API ๊ฐ๊ฐ ์ตœ๋Œ€ `max_images_per_api` ์žฅ.
670
+ โ€ข ๋‹ค์ด์–ด๊ทธ๋žจ: detect_diagram_type_with_score() ์šฐ์„ ์ˆœ์œ„ ์ƒ์œ„ `max_diagrams` ๊ฐœ.
671
+ โ€ข ํ•จ์ˆ˜๊ฐ€ ๋๋‚˜๋ฉด ์ž„์‹œํŒŒ์ผ(.pptx) ๊ฒฝ๋กœ๋ฅผ ๋Œ๋ ค์ค€๋‹ค.
672
+ """
673
+ if not PPTX_AVAILABLE:
674
+ raise ImportError("python-pptx ๋ผ์ด๋ธŒ๋Ÿฌ๋ฆฌ๊ฐ€ ํ•„์š”ํ•ฉ๋‹ˆ๋‹ค (pip install python-pptx)")
675
+
676
+ prs = Presentation()
677
+ theme = DESIGN_THEMES.get(theme_name, DESIGN_THEMES["professional"])
678
+
679
+ # --- ์Šฌ๋ผ์ด๋“œ ์บ”๋ฒ„์Šค ํฌ๊ธฐ (16:9) ---
680
+ prs.slide_width = Inches(10)
681
+ prs.slide_height = Inches(5.625)
682
+
683
+ # ๋‚ด๋ถ€ ์นด์šดํ„ฐ
684
+ img_cnt_3d, img_cnt_flux, dia_cnt = 0, 0, 0
685
+
686
+ # โ–ธโ–ธโ–ธ 0. ๋‹ค์ด์–ด๊ทธ๋žจ ํ•„์š” ์Šฌ๋ผ์ด๋“œ ๋ฏธ๋ฆฌ ์„ ์ •
687
+ diagram_targets: list[tuple[int, str]] = []
688
+ if include_diagrams:
689
+ scored = []
690
+ for idx, s in enumerate(slides_data):
691
+ d_type, score = detect_diagram_type_with_score(s["title"], s["content"])
692
+ if d_type:
693
+ scored.append((idx, d_type, score))
694
+ scored.sort(key=lambda x: x[2], reverse=True)
695
+ diagram_targets = [(i, t) for i, t, _ in scored[:max_diagrams]]
696
+
697
+ # โ–ธโ–ธโ–ธ 1. ํ‘œ์ง€ ์Šฌ๋ผ์ด๋“œ --------------------------------------------------
698
+ cover = prs.slides.add_slide(prs.slide_layouts[0])
699
+ add_gradient_background(cover, theme["colors"]["primary"], theme["colors"]["secondary"])
700
+ cover.shapes.title.text = topic
701
+ cover.shapes.title.text_frame.paragraphs[0].font.size = Pt(36)
702
+ cover.shapes.title.text_frame.paragraphs[0].font.bold = True
703
+ if include_ai_image and (AI_IMAGE_ENABLED or FLUX_API_ENABLED):
704
+ p3d, pph = generate_cover_image_prompts(topic, slides_data)
705
+ img3d, imgph = generate_images_parallel(p3d, pph)
706
+ pick = img3d if img3d and img_cnt_3d < max_images_per_api else imgph
707
+ if pick:
708
+ cover.shapes.add_picture(pick, Inches(6.2), Inches(2.0), width=Inches(3.3))
709
+ if pick == img3d: img_cnt_3d += 1
710
+ else: img_cnt_flux += 1
711
+
712
+ # โ–ธโ–ธโ–ธ 2. ๋ณธ๋ฌธ ์Šฌ๋ผ์ด๋“œ ---------------------------------------------------
713
+ for idx, s in enumerate(slides_data):
714
+ layout = prs.slide_layouts[1]
715
+ sl = prs.slides.add_slide(layout)
716
+ apply_theme_to_slide(sl, theme)
717
+ sl.shapes.title.text = s["title"]
718
+
719
+ # ์™ผ์ชฝ ํ…์ŠคํŠธ
720
+ left = sl.shapes.add_textbox(Inches(0.6), Inches(1.4), Inches(4.4), Inches(3.6))
721
+ tf = left.text_frame
722
+ tf.text = s["content"]
723
+ force_font_size(tf, 16, theme)
724
+
725
+ # ์˜ค๋ฅธ์ชฝ ์‹œ๊ฐ ์š”์†Œ
726
+ right_x, right_y = Inches(5.2), Inches(1.5)
727
+ visual_done = False
728
+
729
+ # ๋‹ค์ด์–ด๊ทธ๋žจ ์šฐ์„ 
730
+ if (idx, _) := next(((i, t) for i, t in diagram_targets if i == idx), None):
731
+ if dia_cnt < max_diagrams:
732
+ j = generate_diagram_json(s["title"], s["content"], _)
733
+ if j:
734
+ p = generate_diagram_locally(j, _, "png")
735
+ if p:
736
+ sl.shapes.add_picture(p, right_x, right_y, width=Inches(4.0))
737
+ visual_done, dia_cnt = True, dia_cnt + 1
738
+
739
+ # ์ด๋ฏธ์ง€ (3D / FLUX)
740
+ if not visual_done and include_flux_images and (img_cnt_3d + img_cnt_flux) < max_images_per_api * 2:
741
+ p3d, pph = generate_diverse_prompt(s["title"], s["content"], idx)
742
+ pic = None
743
+ if img_cnt_3d < max_images_per_api:
744
+ pic = generate_ai_image_via_3d_api(p3d)
745
+ if pic: img_cnt_3d += 1
746
+ if not pic and img_cnt_flux < max_images_per_api:
747
+ pic = generate_flux_image_via_api(pph)
748
+ if pic: img_cnt_flux += 1
749
+ if pic:
750
+ sl.shapes.add_picture(pic, right_x, right_y, width=Inches(4.0))
751
+
752
+ # ์ฐจํŠธ
753
+ if include_charts and s.get("chart_data"):
754
+ create_chart_slide(sl, s["chart_data"], theme)
755
+
756
+ # ์Šฌ๋ผ์ด๋“œ ๋ฒˆํ˜ธ
757
+ num_box = sl.shapes.add_textbox(Inches(9.0), Inches(5.1), Inches(1.0), Inches(0.3))
758
+ num_box.text_frame.text = f"{idx+1}/{len(slides_data)}"
759
+ num_box.text_frame.paragraphs[0].font.size = Pt(10)
760
+
761
+ # โ–ธโ–ธโ–ธ 3. ๋งˆ๋ฌด๋ฆฌ ์Šฌ๋ผ์ด๋“œ -------------------------------------------------
762
+ thanks = prs.slides.add_slide(prs.slide_layouts[5])
763
+ add_gradient_background(thanks, theme["colors"]["secondary"], theme["colors"]["primary"])
764
+ thanks.shapes.title.text = "๊ฐ์‚ฌํ•ฉ๋‹ˆ๋‹ค"
765
+
766
+ # โ–ธโ–ธโ–ธ 4. ์ €์žฅ -----------------------------------------------------------
767
+ tmp = tempfile.NamedTemporaryFile(delete=False, suffix=".pptx")
768
+ prs.save(tmp.name)
769
+ return tmp.name
770
+
771
+ # ------------------------------------------------------------------
772
+ # 2) generate_flux_prompt ํ•จ์ˆ˜ ์ „์ฒด โ˜… ์ˆ˜์ • ์™„๋ฃŒ
773
+ # ------------------------------------------------------------------
774
  def generate_flux_prompt(title: str, content: str, style_key: str) -> str:
775
  """
776
+ Build a FLUX image-generation prompt for one slide, using six pre-defined
777
+ visual styles (Product Design, Mindmap, Mockup, Infographic, Diagram,
778
+ Flowchart). `content` is the raw bullet-point block; โ€˜+โ€™/โ€˜-โ€™/โ€˜โ€ขโ€™/โ€˜โ—โ€™ ๋“ฑ
779
+ ๋ชจ๋“  ๊ธ€๋จธ๋ฆฌ ๊ธฐํ˜ธ๋ฅผ ์ฒ˜๋ฆฌํ•œ๋‹ค.
780
  """
781
+ # 1) clean bullet points (max 8)
782
  bullets = [
783
+ re.sub(r'^[\+\-\โ€ข\โ—]\s*', '', line).strip()
784
  for line in content.splitlines()
785
+ if line.lstrip().startswith(('+', '-', 'โ€ข', 'โ—'))
786
  ][:8]
787
 
 
788
  if bullets:
789
+ nodes_block = '\n'.join(f"- {b}" for b in bullets)
790
+ tree_block = '\n'.join(
791
+ f"{'โ”œโ”€โ”€' if i < len(bullets)-1 else 'โ””โ”€โ”€'} {b}"
792
+ for i, b in enumerate(bullets)
793
+ )
794
  else:
795
+ nodes_block = "- (no explicit bullet points) -"
796
+ tree_block = nodes_block
797
 
798
+ # 2) choose template & inject nodes
799
+ template = EXAMPLE_PROMPTS.get(style_key, EXAMPLE_PROMPTS["Diagram"])
800
+ prompt_body = template.format(nodes=nodes_block, tree=tree_block)
801
 
802
+ # 3) stylistic tail (โ‰ค120 words total)
803
+ tail = (
804
+ "Corporate palette, white background, hand-drawn line style, "
805
+ "clean composition, high-resolution vector."
806
+ )
807
 
808
+ return f"{prompt_body}\n\n{tail}".strip()
 
809
 
 
 
 
810
 
811
 
812
  def generate_flux_image_via_api(prompt: str) -> Optional[str]: