Spaces:
Sleeping
Sleeping
| from pptx import Presentation | |
| from pptx.util import Inches, Pt | |
| from pptx.enum.text import PP_ALIGN | |
| from pptx.dml.color import RGBColor | |
| import tempfile | |
| import logging | |
| import re | |
| logger = logging.getLogger(__name__) | |
| class PresentationGenerator: | |
| def __init__(self): | |
| self.styles = { | |
| "professionnel": { | |
| "title_color": RGBColor(0, 0, 128), | |
| "text_color": RGBColor(0, 0, 0), | |
| "background_color": RGBColor(255, 255, 255), | |
| "font_name": "Calibri" | |
| }, | |
| "moderne": { | |
| "title_color": RGBColor(220, 20, 60), | |
| "text_color": RGBColor(50, 50, 50), | |
| "background_color": RGBColor(240, 240, 240), | |
| "font_name": "Segoe UI" | |
| }, | |
| "creatif": { | |
| "title_color": RGBColor(75, 0, 130), | |
| "text_color": RGBColor(0, 0, 0), | |
| "background_color": RGBColor(255, 250, 240), | |
| "font_name": "Arial" | |
| } | |
| } | |
| def generate_presentation(self, structure, style="professionnel"): | |
| """Génère une présentation PowerPoint à partir de la structure IA""" | |
| try: | |
| logger.info("📄 Initialisation de la présentation...") | |
| prs = Presentation() | |
| style_config = self.styles.get(style, self.styles["professionnel"]) | |
| logger.info("🎯 Ajout de la slide de titre...") | |
| self._add_title_slide(prs, structure, style_config) | |
| logger.info("📄 Ajout des slides de contenu enrichies...") | |
| for i, slide_data in enumerate(structure.get('slides', [])): | |
| logger.info(f" ➕ Slide {i+1}: {slide_data.get('title', 'Sans titre')}") | |
| self._add_enhanced_content_slide(prs, slide_data, style_config, i) | |
| if structure.get('key_points'): | |
| logger.info("🧠 Ajout des points clés enrichis...") | |
| self._add_enhanced_keypoints_slide(prs, structure, style_config) | |
| logger.info("📈 Ajout de la slide statistiques...") | |
| self._add_statistics_slide(prs, structure, style_config) | |
| logger.info("🏁 Ajout de la conclusion enrichie...") | |
| self._add_enhanced_conclusion_slide(prs, structure, style_config) | |
| logger.info("💾 Sauvegarde de la présentation...") | |
| temp_file = tempfile.NamedTemporaryFile(delete=False, suffix='.pptx') | |
| prs.save(temp_file.name) | |
| logger.info(f"✅ Présentation enregistrée dans : {temp_file.name}") | |
| return temp_file.name | |
| except Exception as e: | |
| logger.error(f"❌ Erreur génération présentation: {e}") | |
| raise | |
| def _add_title_slide(self, prs, structure, style_config): | |
| """Ajoute la slide de titre améliorée""" | |
| slide_layout = prs.slide_layouts[0] | |
| slide = prs.slides.add_slide(slide_layout) | |
| title = slide.shapes.title | |
| subtitle = slide.placeholders[1] | |
| title.text = structure.get('title', 'Présentation Générée par IA') | |
| subtitle.text = f"Style: {style_config['font_name']}\nGénéré intelligemment par IA\n{len(structure.get('slides', []))} slides" | |
| def _add_enhanced_content_slide(self, prs, slide_data, style_config, slide_index): | |
| """Ajoute une slide de contenu enrichie""" | |
| slide_layout = prs.slide_layouts[1] | |
| slide = prs.slides.add_slide(slide_layout) | |
| title_shape = slide.shapes.title | |
| content_shape = slide.placeholders[1] if len(slide.placeholders) > 1 else self._create_textbox(slide) | |
| # Titre amélioré | |
| title_shape.text = self._enhance_slide_title(slide_data.get('title', f'Slide {slide_index + 1}'), slide_index) | |
| # Contenu enrichi | |
| enhanced_content = self._enhance_slide_content(slide_data.get('content', ''), slide_index) | |
| content_shape.text = enhanced_content | |
| self._apply_style(title_shape, style_config, is_title=True) | |
| self._apply_style(content_shape, style_config, is_title=False) | |
| def _enhance_slide_title(self, title, slide_index): | |
| """Améliore les titres des slides""" | |
| title_enhancements = { | |
| 0: ["🚀 Introduction", "📋 Aperçu Général", "🎯 Présentation du Sujet"], | |
| 1: ["🔍 Analyse Détaillée", "📊 Points Clés", "💡 Insights Principaux"], | |
| 2: ["🏗️ Développement", "📈 Données et Faits", "🔬 Analyse Approfondie"], | |
| 3: ["💎 Synthèse", "🎖️ Points Forts", "📝 Résumé Exécutif"] | |
| } | |
| base_title = title_enhancements.get(slide_index, ["📄 Slide", "💼 Contenu", "📖 Section"]) | |
| return f"{base_title[0]} : {title}" | |
| def _enhance_slide_content(self, content, slide_index): | |
| """Enrichit le contenu des slides""" | |
| if not content or len(content.strip()) < 10: | |
| default_contents = [ | |
| "Présentation du contexte et des objectifs\n• Introduction au sujet principal\n• Définition des enjeux clés\n• Présentation de la structure", | |
| "Analyse des points essentiels\n• Données chiffrées et statistiques\n• Tendances observées\n• Perspectives d'évolution", | |
| "Développement des concepts clés\n• Études de cas concrètes\n• Recommandations pratiques\n• Implications stratégiques", | |
| "Synthèse des éléments majeurs\n• Points à retenir\n• Actions recommandées\n• Prochaines étapes" | |
| ] | |
| return default_contents[slide_index % len(default_contents)] | |
| # Enrichir le contenu existant | |
| enhancements = [ | |
| "\n\n🎯 Points clés à retenir :\n• Information principale\n• Donnée significative\n• Insight important", | |
| "\n\n📊 Éléments marquants :\n• Chiffre clé\n• Tendance observée\n• Recommandation", | |
| "\n\n💡 Actions recommandées :\n• Mesure concrète\n• Stratégie à adopter\n• Perspective future", | |
| "\n\n🌟 Impact et valeur :\n• Bénéfice attendu\n• Résultat potentiel\n• Contribution majeure" | |
| ] | |
| enhanced_content = content | |
| if len(content) > 100: | |
| # Ajouter des puces si le contenu est long | |
| sentences = [s.strip() for s in content.split('.') if s.strip()] | |
| if len(sentences) > 2: | |
| enhanced_content = "• " + "\n• ".join(sentences[:4]) | |
| return enhanced_content + enhancements[slide_index % len(enhancements)] | |
| def _add_enhanced_keypoints_slide(self, prs, structure, style_config): | |
| """Ajoute la slide des points clés enrichie""" | |
| slide_layout = prs.slide_layouts[1] | |
| slide = prs.slides.add_slide(slide_layout) | |
| title_shape = slide.shapes.title | |
| content_shape = slide.placeholders[1] if len(slide.placeholders) > 1 else self._create_textbox(slide) | |
| title_shape.text = "🎯 Points Clés Identifiés par IA" | |
| # Points clés enrichis | |
| key_points = structure.get('key_points', []) | |
| enhanced_points = [] | |
| for i, point in enumerate(key_points[:10]): # Limiter à 10 points max | |
| icons = ["🔸", "🌟", "💡", "📌", "🎯", "⚡", "✅", "📊", "🔍", "🏆"] | |
| enhanced_points.append(f"{icons[i % len(icons)]} {point}") | |
| content_text = "Principaux insights extraits de l'analyse :\n\n" + "\n".join(enhanced_points) | |
| # Ajouter une conclusion sur les points clés | |
| content_text += f"\n\n📈 Synthèse :\n• {len(key_points)} thèmes majeurs identifiés\n• Analyse sémantique avancée\n• Priorisation intelligente" | |
| content_shape.text = content_text | |
| self._apply_style(title_shape, style_config, is_title=True) | |
| self._apply_style(content_shape, style_config, is_title=False) | |
| def _add_statistics_slide(self, prs, structure, style_config): | |
| """Ajoute une slide avec les statistiques d'analyse""" | |
| slide_layout = prs.slide_layouts[1] | |
| slide = prs.slides.add_slide(slide_layout) | |
| title_shape = slide.shapes.title | |
| content_shape = slide.placeholders[1] if len(slide.placeholders) > 1 else self._create_textbox(slide) | |
| title_shape.text = "📊 Métriques d'Analyse" | |
| stats = structure.get('analysis_metadata', {}).get('statistics', {}) | |
| content_text = "Analyse quantitative du contenu :\n\n" | |
| metrics = [ | |
| f"📝 {stats.get('word_count', 0)} mots analysés", | |
| f"💬 {stats.get('sentence_count', 0)} phrases traitées", | |
| f"📄 {stats.get('paragraph_count', 0)} paragraphes examinés", | |
| f"🎯 {len(structure.get('key_points', []))} thèmes clés identifiés", | |
| f"🔄 {len(structure.get('slides', []))} slides générées", | |
| f"⚡ Complexité : {stats.get('complexity_score', 0.5):.1f}/1.0" | |
| ] | |
| content_text += "\n".join(metrics) | |
| content_text += "\n\n🔍 Méthodologie :\n• Analyse sémantique IA\n• Extraction de concepts clés\n• Structuration intelligente" | |
| content_shape.text = content_text | |
| self._apply_style(title_shape, style_config, is_title=True) | |
| self._apply_style(content_shape, style_config, is_title=False) | |
| def _add_enhanced_conclusion_slide(self, prs, structure, style_config): | |
| """Ajoute la slide de conclusion enrichie""" | |
| slide_layout = prs.slide_layouts[1] | |
| slide = prs.slides.add_slide(slide_layout) | |
| title_shape = slide.shapes.title | |
| content_shape = slide.placeholders[1] if len(slide.placeholders) > 1 else self._create_textbox(slide) | |
| title_shape.text = "🏁 Conclusion & Perspectives" | |
| conclusion_text = "Synthèse de la présentation :\n\n" | |
| conclusion_text += "✅ Analyse complète réalisée avec succès\n" | |
| conclusion_text += "🎯 Points essentiels mis en lumière\n" | |
| conclusion_text += "💡 Insights actionnables identifiés\n\n" | |
| conclusion_text += "Prochaines étapes recommandées :\n" | |
| conclusion_text += "• Approfondir les points clés\n" | |
| conclusion_text += "• Mettre en œuvre les recommandations\n" | |
| conclusion_text += "• Mesurer l'impact des actions\n\n" | |
| conclusion_text += "🧠 Présentation générée automatiquement\n" | |
| conclusion_text += "avec technologie IA avancée\n" | |
| conclusion_text += "Lab_Math & Labhp - CIE Label_Bertoua" | |
| content_shape.text = conclusion_text | |
| self._apply_style(title_shape, style_config, is_title=True) | |
| self._apply_style(content_shape, style_config, is_title=False) | |
| def _create_textbox(self, slide): | |
| """Crée une textbox manuelle si le placeholder n'existe pas""" | |
| return slide.shapes.add_textbox(Inches(1), Inches(1.5), Inches(8), Inches(5)) | |
| def _apply_style(self, shape, style_config, is_title=True): | |
| """Applique le style aux éléments textuels""" | |
| if not hasattr(shape, "text_frame") or shape.text_frame is None: | |
| logger.warning("⚠️ Shape sans text_frame, style non appliqué.") | |
| return | |
| try: | |
| for paragraph in shape.text_frame.paragraphs: | |
| for run in paragraph.runs: | |
| if run.font: | |
| run.font.name = style_config["font_name"] | |
| run.font.color.rgb = ( | |
| style_config["title_color"] if is_title else style_config["text_color"] | |
| ) | |
| run.font.size = Pt(32 if is_title else 16) | |
| run.font.bold = is_title | |
| except Exception as e: | |
| logger.warning(f"❌ Échec de l'application du style: {e}") |