Spaces:
Sleeping
Sleeping
| import gradio as gr | |
| import os | |
| import tempfile | |
| from datetime import datetime | |
| import logging | |
| import json | |
| import PyPDF2 | |
| from docx import Document | |
| # Configuration logging | |
| logging.basicConfig(level=logging.INFO) | |
| logger = logging.getLogger(__name__) | |
| # Initialisation des services | |
| from ai_enhance import AIEnhance | |
| from presentation_generator import PresentationGenerator | |
| ai_service = AIEnhance() | |
| presentation_generator = PresentationGenerator() | |
| # Code secret | |
| CODE_SECRET = "32015" # code à cinq chiffres | |
| def verifier_code(code): | |
| """Vérifie si le code entré est correct""" | |
| if code == CODE_SECRET: | |
| return True, "🔓 Accès autorisé !" | |
| else: | |
| return False, "❌ Code incorrect. Veuillez réessayer." | |
| # FONCTIONS D'EXTRACTION DE TEXTE CORRIGÉES | |
| def extract_text_from_pdf(file_path): | |
| """Extrait le texte d'un fichier PDF""" | |
| try: | |
| with open(file_path, 'rb') as file: | |
| pdf_reader = PyPDF2.PdfReader(file) | |
| text = "" | |
| for page in pdf_reader.pages: | |
| text += page.extract_text() + "\n" | |
| return text.strip() | |
| except Exception as e: | |
| logger.error(f"Erreur extraction PDF: {e}") | |
| raise Exception(f"Erreur lors de l'extraction du PDF: {str(e)}") | |
| def extract_text_from_docx(file_path): | |
| """Extrait le texte d'un fichier Word""" | |
| try: | |
| doc = Document(file_path) | |
| text = "" | |
| for paragraph in doc.paragraphs: | |
| text += paragraph.text + "\n" | |
| return text.strip() | |
| except Exception as e: | |
| logger.error(f"Erreur extraction DOCX: {e}") | |
| raise Exception(f"Erreur lors de l'extraction du document Word: {str(e)}") | |
| def extract_text_from_txt(file_path): | |
| """Extrait le texte d'un fichier texte""" | |
| try: | |
| with open(file_path, 'r', encoding='utf-8') as file: | |
| return file.read().strip() | |
| except Exception as e: | |
| logger.error(f"Erreur extraction TXT: {e}") | |
| raise Exception(f"Erreur lors de l'extraction du fichier texte: {str(e)}") | |
| def extract_text_from_file(file_obj): | |
| """Extrait le texte selon le format du fichier - VERSION CORRIGÉE""" | |
| try: | |
| # GESTION CORRIGÉE DU TYPE DE FICHIER | |
| if hasattr(file_obj, 'name'): | |
| file_path = file_obj.name | |
| else: | |
| file_path = file_obj # C'est déjà un chemin | |
| file_extension = os.path.splitext(file_path)[1].lower() | |
| logger.info(f"📁 Extraction du fichier: {file_path}") | |
| if file_extension == '.pdf': | |
| return extract_text_from_pdf(file_path) | |
| elif file_extension in ['.doc', '.docx']: | |
| return extract_text_from_docx(file_path) | |
| elif file_extension == '.txt': | |
| return extract_text_from_txt(file_path) | |
| else: | |
| raise Exception(f"Format non supporté: {file_extension}") | |
| except Exception as e: | |
| logger.error(f"❌ Erreur extraction: {e}") | |
| raise | |
| def create_presentation_structure(texte): | |
| """Crée la structure de présentation avec analyse IA""" | |
| try: | |
| # ESSAYER DIFFÉRENTES MÉTHODES POUR TROUVER CELLE QUI FONCTIONNE | |
| if hasattr(ai_service, '_basic_analysis'): | |
| analysis = ai_service._basic_analysis(texte) | |
| elif hasattr(ai_service, 'analyze_text_advanced'): | |
| analysis = ai_service.analyze_text_advanced(texte) | |
| else: | |
| # Créer une analyse de base manuellement | |
| analysis = { | |
| "statistics": { | |
| "word_count": len(texte.split()), | |
| "sentence_count": texte.count('.'), | |
| "paragraph_count": len([p for p in texte.split('\n\n') if p.strip()]) | |
| }, | |
| "keywords": ["texte", "analyse", "présentation"], | |
| "recommended_structure": { | |
| "slides": [ | |
| {"title": "Introduction", "content": "Présentation du sujet"}, | |
| {"title": "Développement", "content": "Points principaux"}, | |
| {"title": "Conclusion", "content": "Synthèse"} | |
| ], | |
| "recommended_style": "professionnel" | |
| }, | |
| "content_analysis": "général" | |
| } | |
| summary = ai_service.smart_summarize(texte) | |
| structure = { | |
| "title": f"Présentation: {analysis['content_analysis'].capitalize()}", | |
| "slides": analysis["recommended_structure"]["slides"], | |
| "key_points": analysis["keywords"][:8], | |
| "style_recommendation": analysis["recommended_structure"]["recommended_style"], | |
| "analysis_metadata": analysis | |
| } | |
| return structure, summary | |
| except Exception as e: | |
| logger.error(f"Erreur dans create_presentation_structure: {e}") | |
| # Retourner une structure par défaut en cas d'erreur | |
| default_structure = { | |
| "title": "Présentation Générée par IA", | |
| "slides": [ | |
| {"title": "Introduction", "content": "Votre texte a été analysé avec succès"}, | |
| {"title": "Points Principaux", "content": "Les insights clés ont été extraits"}, | |
| {"title": "Conclusion", "content": "Synthèse des éléments importants"} | |
| ], | |
| "key_points": ["analyse", "texte", "présentation"], | |
| "analysis_metadata": {"statistics": {"word_count": len(texte.split())}} | |
| } | |
| return default_structure, texte[:200] + "..." | |
| def generate_presentation_gradio(texte, style="professionnel"): | |
| """Version Gradio améliorée avec prévisualisation""" | |
| try: | |
| if not texte or len(texte.strip()) < 50: | |
| return None, None, "❌ Veuillez entrer au moins 50 caractères." | |
| logger.info(f"🚀 Génération IA pour {len(texte)} caractères") | |
| # Générer la structure | |
| structure, summary = create_presentation_structure(texte) | |
| filename = presentation_generator.generate_presentation(structure, style) | |
| # Créer une belle prévisualisation | |
| preview_html = create_preview_html(structure, summary) | |
| logger.info("✅ Présentation générée avec succès!") | |
| return filename, preview_html, f"🎉 Présentation générée! ({len(structure['slides'])} slides)" | |
| except Exception as e: | |
| logger.error(f"❌ Erreur lors de la génération: {e}") | |
| return None, None, f"❌ Erreur: {str(e)}" | |
| def create_preview_html(structure, summary): | |
| """Crée une belle prévisualisation HTML des slides""" | |
| html_content = f""" | |
| <div style="font-family: Arial, sans-serif; max-width: 800px; margin: 0 auto;"> | |
| <h2 style="color: #2E86AB; border-bottom: 2px solid #2E86AB; padding-bottom: 10px;"> | |
| 📊 Aperçu de votre Présentation | |
| </h2> | |
| <div style="background: #f8f9fa; padding: 15px; border-radius: 10px; margin-bottom: 20px;"> | |
| <h3 style="color: #2E86AB;">🎯 Titre Principal</h3> | |
| <p style="font-size: 18px; font-weight: bold;">{structure['title']}</p> | |
| </div> | |
| <div style="background: #e8f4f8; padding: 15px; border-radius: 10px; margin-bottom: 20px;"> | |
| <h3 style="color: #2E86AB;">📝 Résumé IA</h3> | |
| <p>{summary}</p> | |
| </div> | |
| <h3 style="color: #2E86AB;">🔄 Structure des Slides</h3> | |
| """ | |
| # Ajouter chaque slide | |
| for i, slide in enumerate(structure['slides']): | |
| html_content += f""" | |
| <div style="background: white; border: 2px solid #2E86AB; border-radius: 10px; padding: 15px; margin: 10px 0;"> | |
| <div style="display: flex; align-items: center; margin-bottom: 10px;"> | |
| <span style="background: #2E86AB; color: white; width: 30px; height: 30px; border-radius: 50%; display: flex; align-items: center; justify-content: center; font-weight: bold;"> | |
| {i+1} | |
| </span> | |
| <h4 style="margin: 0 0 0 10px; color: #2E86AB;">{slide['title']}</h4> | |
| </div> | |
| <p style="margin: 0; color: #555;">{slide['content']}</p> | |
| </div> | |
| """ | |
| # Ajouter les points clés | |
| html_content += f""" | |
| <div style="background: #fff3cd; padding: 15px; border-radius: 10px; margin-top: 20px;"> | |
| <h3 style="color: #856404;">🎯 Points Clés Identifiés</h3> | |
| <div style="display: flex; flex-wrap: wrap; gap: 8px;"> | |
| """ | |
| for point in structure['key_points']: | |
| html_content += f'<span style="background: #856404; color: white; padding: 5px 10px; border-radius: 15px; font-size: 12px;">{point}</span>' | |
| html_content += """ | |
| </div> | |
| </div> | |
| </div> | |
| """ | |
| return html_content | |
| ##### FONCTIONS UPLOAD CORRIGÉES ##### | |
| def handle_file_upload(files): | |
| """Gère l'upload de fichiers et extrait le texte - VERSION CORRIGÉE""" | |
| if not files: | |
| return None, None, "❌ Aucun fichier sélectionné." | |
| try: | |
| # CORRECTION : Gradio passe une liste, prendre le premier élément | |
| file_obj = files[0] if isinstance(files, list) else files | |
| extracted_text = extract_text_from_file(file_obj) | |
| if not extracted_text or len(extracted_text.strip()) < 10: | |
| return None, None, "❌ Le fichier semble vide ou ne contient pas de texte extractible." | |
| # Statistiques | |
| word_count = len(extracted_text.split()) | |
| char_count = len(extracted_text) | |
| message = f"✅ Fichier traité avec succès! 📊 {word_count} mots, {char_count} caractères" | |
| return extracted_text, extracted_text, message | |
| except Exception as e: | |
| logger.error(f"Erreur upload: {e}") | |
| return None, None, f"❌ Erreur: {str(e)}" | |
| def generate_from_uploaded_text(analyzed_text, style="professionnel"): | |
| """Génère la présentation à partir du texte uploadé""" | |
| return generate_presentation_gradio(analyzed_text, style) | |
| def analyze_text_gradio(texte): | |
| """Version Gradio de votre api_analyze()""" | |
| try: | |
| if not texte or len(texte.strip()) < 10: | |
| return "❌ Texte trop court. Minimum 10 caractères." | |
| structure, summary = create_presentation_structure(texte) | |
| # Formatage pour l'interface Gradio | |
| result = f""" | |
| ## 📊 Analyse IA du Texte | |
| **Statistiques:** | |
| - {structure['analysis_metadata']['statistics']['word_count']} mots | |
| - {structure['analysis_metadata']['statistics']['sentence_count']} phrases | |
| - {structure['analysis_metadata']['statistics']['paragraph_count']} paragraphes | |
| **🎯 Thèmes identifiés:** | |
| {', '.join(structure['key_points'][:8])} | |
| **📝 Résumé:** | |
| {summary} | |
| **🏗️ Structure proposée:** | |
| """ | |
| for i, slide in enumerate(structure['slides']): | |
| result += f"\n{i+1}. **{slide['title']}** - {slide['content'][:100]}..." | |
| return result | |
| except Exception as e: | |
| return f"❌ Erreur d'analyse: {str(e)}" | |
| # FONCTION CORRIGÉE POUR LES INFOS DE FICHIER | |
| def update_file_info(files): | |
| """Met à jour les informations du fichier uploadé - VERSION CORRIGÉE""" | |
| if not files: | |
| return "Aucun fichier sélectionné" | |
| try: | |
| # CORRECTION : Gradio passe une liste, prendre le premier élément | |
| file_obj = files[0] if isinstance(files, list) else files | |
| # Gestion des différents types d'objets fichier | |
| if hasattr(file_obj, 'name'): | |
| file_path = file_obj.name | |
| elif isinstance(file_obj, str): | |
| file_path = file_obj | |
| else: | |
| return "Type de fichier non supporté" | |
| file_name = os.path.basename(file_path) | |
| file_size = os.path.getsize(file_path) / 1024 # KB | |
| return f"📄 {file_name} ({file_size:.1f} KB)" | |
| except Exception as e: | |
| logger.error(f"Erreur dans update_file_info: {e}") | |
| return "Erreur lors de la lecture du fichier" | |
| # INTERFACE PRINCIPALE AVEC AUTHENTIFICATION | |
| with gr.Blocks(theme=gr.themes.Soft(), title="Générateur de Présentation IA") as demo: | |
| # Écran d'authentification | |
| with gr.Column(visible=True) as auth_screen: | |
| gr.Markdown("# 🔒 Accès Sécurisé") | |
| gr.Markdown("Veuillez entrer le code d'accès à 5 chiffres") | |
| with gr.Row(): | |
| code_input = gr.Textbox( | |
| label="Code d'accès", | |
| type="text", | |
| max_lines=1, | |
| placeholder="Entrez les 5 chiffres...", | |
| elem_id="code_input" | |
| ) | |
| verifier_btn = gr.Button("Vérifier", variant="primary") | |
| message_sortie = gr.Textbox(label="Statut", interactive=False) | |
| # Application principale (cachée au début) | |
| with gr.Column(visible=False) as app_screen: | |
| gr.Markdown(""" | |
| # 🧠 Générateur de Présentation IA Intelligente | |
| **Powered by Lab_Math_and Labhp & CIE Label_Bertoua** | |
| Transformez votre texte en présentation PowerPoint professionnelle en quelques secondes ! | |
| """) | |
| with gr.Tab("🚀 Générer à partir de Texte"): | |
| with gr.Row(): | |
| with gr.Column(): | |
| text_input = gr.Textbox( | |
| label="📝 Collez votre texte ici", | |
| placeholder="Collez ou tapez votre texte, article, rapport... (minimum 50 caractères)", | |
| lines=12, | |
| max_lines=20 | |
| ) | |
| style_dropdown = gr.Dropdown( | |
| choices=["professionnel", "moderne", "creatif"], | |
| label="🎨 Style de présentation", | |
| value="professionnel", | |
| info="Choisissez le style visuel de votre présentation" | |
| ) | |
| generate_btn = gr.Button("🚀 Générer la Présentation", variant="primary", size="lg") | |
| with gr.Column(): | |
| output_file = gr.File(label="📥 Télécharger la Présentation", file_types=[".pptx"]) | |
| preview_output = gr.HTML(label="👁️ Aperçu de la Structure") | |
| output_message = gr.Textbox(label="📋 Statut", interactive=False) | |
| generate_btn.click( | |
| fn=generate_presentation_gradio, | |
| inputs=[text_input, style_dropdown], | |
| outputs=[output_file, preview_output, output_message] | |
| ) | |
| with gr.Tab("📁 Générer à partir de Fichier"): | |
| gr.Markdown("### 📤 Uploader votre document") | |
| gr.Markdown(""" | |
| **Formats supportés:** | |
| - 📄 PDF (rapports, articles) | |
| - 📝 DOC/DOCX (documents Word) | |
| - 📋 TXT (fichiers texte) | |
| """) | |
| with gr.Row(): | |
| with gr.Column(): | |
| upload_button = gr.UploadButton( | |
| "📁 Choisir un fichier", | |
| file_types=[".pdf", ".doc", ".docx", ".txt"], | |
| file_count="single", | |
| size="lg" | |
| ) | |
| uploaded_file_info = gr.Textbox( | |
| label="📋 Fichier sélectionné", | |
| interactive=False, | |
| value="Aucun fichier sélectionné" | |
| ) | |
| extract_btn = gr.Button("🔍 Extraire et Analyser", variant="primary") | |
| with gr.Column(): | |
| extracted_text = gr.Textbox( | |
| label="📝 Texte extrait", | |
| interactive=False, | |
| lines=8, | |
| max_lines=12, | |
| placeholder="Le texte extrait apparaîtra ici..." | |
| ) | |
| upload_status = gr.Textbox( | |
| label="📊 Statut extraction", | |
| interactive=False, | |
| value="En attente de fichier..." | |
| ) | |
| with gr.Row(): | |
| file_style_dropdown = gr.Dropdown( | |
| choices=["professionnel", "moderne", "creatif"], | |
| label="🎨 Style de présentation", | |
| value="professionnel" | |
| ) | |
| generate_from_file_btn = gr.Button("🚀 Générer la Présentation", variant="primary") | |
| with gr.Row(): | |
| file_output = gr.File(label="📥 Présentation Générée", file_types=[".pptx"]) | |
| file_preview = gr.HTML(label="👁️ Aperçu") | |
| file_message = gr.Textbox( | |
| label="📋 Statut génération", | |
| interactive=False, | |
| value="En attente..." | |
| ) | |
| # GESTION DES INTERACTIONS UPLOAD - VERSION CORRIGÉE | |
| upload_button.upload( | |
| fn=update_file_info, | |
| inputs=[upload_button], | |
| outputs=[uploaded_file_info] | |
| ) | |
| extract_btn.click( | |
| fn=handle_file_upload, | |
| inputs=[upload_button], | |
| outputs=[extracted_text, extracted_text, upload_status] | |
| ) | |
| generate_from_file_btn.click( | |
| fn=generate_from_uploaded_text, | |
| inputs=[extracted_text, file_style_dropdown], | |
| outputs=[file_output, file_preview, file_message] | |
| ) | |
| def gerer_acces(code): | |
| """Gère l'authentification et affiche l'application si le code est correct""" | |
| est_valide, message = verifier_code(code) | |
| return ( | |
| message, | |
| gr.update(visible=not est_valide), # Cacher l'écran d'auth | |
| gr.update(visible=est_valide) # Afficher l'application | |
| ) | |
| # Lier le bouton de vérification | |
| verifier_btn.click( | |
| fn=gerer_acces, | |
| inputs=[code_input], | |
| outputs=[message_sortie, auth_screen, app_screen] | |
| ) | |
| if __name__ == "__main__": | |
| demo.launch( | |
| server_name="0.0.0.0", | |
| server_port=7860, | |
| share=False | |
| ) |