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"""
📊 Aperçu de votre Présentation
🎯 Titre Principal
{structure['title']}
🔄 Structure des Slides
"""
# Ajouter chaque slide
for i, slide in enumerate(structure['slides']):
html_content += f"""
{i+1}
{slide['title']}
{slide['content']}
"""
# Ajouter les points clés
html_content += f"""
🎯 Points Clés Identifiés
"""
for point in structure['key_points']:
html_content += f'{point}'
html_content += """
"""
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
)