| import gradio as gr
|
| import os
|
| import re
|
| import time
|
| import nltk
|
| from pptx import Presentation
|
| from pptx.util import Inches, Pt
|
| from pptx.dml.color import RGBColor
|
| from pptx.enum.text import PP_ALIGN
|
| from pptx.enum.shapes import MSO_SHAPE
|
| import PyPDF2
|
| from docx import Document
|
| from typing import List, Dict, Any
|
| import hashlib
|
|
|
|
|
|
|
|
|
| class SmartAIEnhance:
|
| def __init__(self):
|
| try:
|
| nltk.download('punkt', quiet=True)
|
| nltk.download('punkt_tab', quiet=True)
|
| nltk.download('averaged_perceptron_tagger', quiet=True)
|
| nltk.download('maxent_ne_chunker', quiet=True)
|
| nltk.download('words', quiet=True)
|
| except:
|
| pass
|
|
|
| def detect_document_features(self, text: str) -> Dict[str, Any]:
|
| """Détecte les caractéristiques spécifiques du document"""
|
| features = {
|
| 'has_abstract': bool(re.search(r'(résumé|abstract|summary)', text[:1000], re.I)),
|
| 'has_methodology': bool(re.search(r'(méthodologie|methodology|méthode|method)', text, re.I)),
|
| 'has_results': bool(re.search(r'(résultats|results|findings)', text, re.I)),
|
| 'has_conclusion': bool(re.search(r'(conclusion|discussion)', text, re.I)),
|
| 'has_references': bool(re.search(r'(références|references|bibliographie)', text[-2000:], re.I)),
|
| 'has_figures': len(re.findall(r'(figure|fig\.|tableau|table)', text, re.I)) > 5,
|
| 'word_count': len(text.split()),
|
| 'sentence_count': len(nltk.sent_tokenize(text)) if nltk else len(text.split('.'))
|
| }
|
| return features
|
|
|
| def extract_metadata(self, text: str, category: str) -> Dict[str, Any]:
|
| """Extrait les métadonnées spécifiques au type de document"""
|
| metadata = {
|
| 'title': self._extract_title(text),
|
| 'authors': self._extract_authors(text) if category in ["Article Scientifique"] else [],
|
| 'date': self._extract_date(text),
|
| 'keywords': self._extract_keywords(text, category),
|
| 'main_findings': self._extract_findings(text, category)
|
| }
|
| return metadata
|
|
|
| def _extract_title(self, text: str) -> str:
|
| """Extrait le titre probable du document"""
|
| lines = text.split('\n')
|
| for line in lines[:10]:
|
| if len(line) > 20 and len(line) < 200 and line[0].isupper():
|
| return line.strip()
|
| return nltk.sent_tokenize(text)[0][:100] if nltk else text[:100]
|
|
|
| def _extract_authors(self, text: str) -> List[str]:
|
| """Extrait les auteurs (pour articles scientifiques)"""
|
| author_pattern = r'(?:authors?|auteurs?):?\s*([^\n]+)'
|
| match = re.search(author_pattern, text[:2000], re.I)
|
| if match:
|
| authors = [a.strip() for a in match.group(1).split(',')]
|
| return authors[:5]
|
| return []
|
|
|
| def _extract_date(self, text: str) -> str:
|
| """Extrait la date du document"""
|
| date_pattern = r'\b(0?[1-9]|[12][0-9]|3[01])[/-](0?[1-9]|1[012])[/-](19|20)\d{2}\b'
|
| match = re.search(date_pattern, text)
|
| return match.group(0) if match else time.strftime("%d/%m/%Y")
|
|
|
| def _extract_keywords(self, text: str, category: str) -> List[str]:
|
| """Extrait les mots-clés spécifiques au domaine"""
|
|
|
| kw_pattern = r'(?:mots[-\s]clés?|keywords?):?\s*([^\n]+)'
|
| match = re.search(kw_pattern, text[:2000], re.I)
|
| if match:
|
| return [k.strip() for k in match.group(1).split(',')[:6]]
|
|
|
|
|
| words = re.findall(r'\b[A-Za-z]{4,}\b', text.lower())
|
| from collections import Counter
|
| common = Counter(words).most_common(10)
|
| return [w for w, c in common if c > 3][:6]
|
|
|
| def _extract_findings(self, text: str, category: str) -> List[str]:
|
| """Extrait les principales conclusions/découvertes"""
|
| finding_patterns = [
|
| r'(?:conclusion|résultat|finding|我们发现|我们发现|我们发现)[s]?:\s*([^.]+)',
|
| r'(?:montre|indique|suggère|suggere|démontre|demontre)[^.]*\.',
|
| r'(?:important|significatif|crucial)[^.]*\.'
|
| ]
|
|
|
| findings = []
|
| for pattern in finding_patterns:
|
| matches = re.findall(pattern, text, re.I)
|
| findings.extend([m.strip()[:100] for m in matches if len(m.strip()) > 20])
|
|
|
| return findings[:5]
|
|
|
| def create_adaptive_outline(self, text: str, category: str) -> Dict[str, Any]:
|
| """Crée un plan adaptatif basé sur le type de document"""
|
| text = text.replace('\n', ' ').strip()
|
| features = self.detect_document_features(text)
|
| metadata = self.extract_metadata(text, category)
|
|
|
|
|
| plans = {
|
| "Article Scientifique": {
|
| "title_slide": ["Titre", "Auteurs", "Affiliation", "Date"],
|
| "slides": [
|
| {"title": "RÉSUMÉ & INTRODUCTION", "content_type": "abstract", "icon": "📄"},
|
| {"title": "CONTEXTE & PROBLÉMATIQUE", "content_type": "context", "icon": "❓"},
|
| {"title": "MÉTHODOLOGIE", "content_type": "methods", "icon": "🔬"},
|
| {"title": "RÉSULTATS PRINCIPAUX", "content_type": "results", "icon": "📊"},
|
| {"title": "DISCUSSION & ANALYSE", "content_type": "discussion", "icon": "💭"},
|
| {"title": "CONCLUSIONS & PERSPECTIVES", "content_type": "conclusion", "icon": "🎯"},
|
| {"title": "RÉFÉRENCES CLÉS", "content_type": "references", "icon": "📚"}
|
| ]
|
| },
|
|
|
| "Communication Scientifique": {
|
| "title_slide": ["Titre", "Auteur", "Événement", "Date"],
|
| "slides": [
|
| {"title": "CONTEXTE & ENJEUX", "content_type": "context", "icon": "🌍"},
|
| {"title": "MESSAGE CLÉ", "content_type": "key_message", "icon": "💡"},
|
| {"title": "DONNÉES PRINCIPALES", "content_type": "results", "icon": "📈"},
|
| {"title": "IMPACT & APPLICATIONS", "content_type": "impact", "icon": "⚡"},
|
| {"title": "CONCLUSION", "content_type": "conclusion", "icon": "🎯"},
|
| {"title": "PERSPECTIVES", "content_type": "perspectives", "icon": "🔮"}
|
| ]
|
| },
|
|
|
| "Rapport d'Étude": {
|
| "title_slide": ["Titre de l'étude", "Commanditaire", "Équipe", "Date"],
|
| "slides": [
|
| {"title": "CADRE DE L'ÉTUDE", "content_type": "context", "icon": "📋"},
|
| {"title": "OBJECTIFS & MÉTHODOLOGIE", "content_type": "methods", "icon": "🎯"},
|
| {"title": "DONNÉES COLLECTÉES", "content_type": "data", "icon": "📊"},
|
| {"title": "ANALYSE TECHNIQUE", "content_type": "analysis", "icon": "🔍"},
|
| {"title": "CONSTATS PRINCIPAUX", "content_type": "findings", "icon": "📌"},
|
| {"title": "RECOMMANDATIONS", "content_type": "recommendations", "icon": "💡"},
|
| {"title": "AXES STRATÉGIQUES", "content_type": "strategic", "icon": "🎯"},
|
| {"title": "ANNEXES TECHNIQUES", "content_type": "appendices", "icon": "📎"}
|
| ]
|
| },
|
|
|
| "Rapport de Réunion": {
|
| "title_slide": ["Objet", "Date", "Lieu", "Participants"],
|
| "slides": [
|
| {"title": "ORDRE DU JOUR", "content_type": "agenda", "icon": "📅"},
|
| {"title": "PARTICIPANTS", "content_type": "participants", "icon": "👥"},
|
| {"title": "POINTS DISCUTÉS", "content_type": "discussions", "icon": "💬"},
|
| {"title": "DÉCISIONS PRISES", "content_type": "decisions", "icon": "✅"},
|
| {"title": "PLAN D'ACTION", "content_type": "actions", "icon": "📋"},
|
| {"title": "PROCHAINE RÉUNION", "content_type": "next", "icon": "⏰"}
|
| ]
|
| },
|
|
|
| "Cours pour Étudiants": {
|
| "title_slide": ["Titre du cours", "Niveau", "Enseignant", "Date"],
|
| "slides": [
|
| {"title": "OBJECTIFS PÉDAGOGIQUES", "content_type": "objectives", "icon": "🎓"},
|
| {"title": "PLAN DU COURS", "content_type": "plan", "icon": "📑"},
|
| {"title": "CONCEPTS CLÉS", "content_type": "concepts", "icon": "🔑"},
|
| {"title": "EXEMPLES ILLUSTRATIFS", "content_type": "examples", "icon": "💡"},
|
| {"title": "EXERCICES PRATIQUES", "content_type": "exercises", "icon": "✏️"},
|
| {"title": "RÉSUMÉ", "content_type": "summary", "icon": "📌"},
|
| {"title": "RÉFÉRENCES", "content_type": "references", "icon": "📚"},
|
| {"title": "QUESTIONS/RÉPONSES", "content_type": "qa", "icon": "❓"}
|
| ]
|
| },
|
|
|
| "Note de Synthèse": {
|
| "title_slide": ["Titre", "Destinataire", "Rédacteur", "Date"],
|
| "slides": [
|
| {"title": "CONTEXTE", "content_type": "context", "icon": "🌍"},
|
| {"title": "ENJEUX PRINCIPAUX", "content_type": "challenges", "icon": "⚠️"},
|
| {"title": "ANALYSE SYNTHÉTIQUE", "content_type": "analysis", "icon": "📊"},
|
| {"title": "POINTS D'ATTENTION", "content_type": "attention", "icon": "🔍"},
|
| {"title": "RECOMMANDATIONS", "content_type": "recommendations", "icon": "💡"},
|
| {"title": "SUITES À DONNER", "content_type": "next_steps", "icon": "⏩"}
|
| ]
|
| },
|
|
|
| "Document de Travail": {
|
| "title_slide": ["Titre", "Groupe", "Version", "Date"],
|
| "slides": [
|
| {"title": "CONTEXTE DU DOCUMENT", "content_type": "context", "icon": "📄"},
|
| {"title": "OBJECTIFS VISÉS", "content_type": "objectives", "icon": "🎯"},
|
| {"title": "ÉTAT D'AVANCEMENT", "content_type": "progress", "icon": "📈"},
|
| {"title": "POINTS DE BLOCAGE", "content_type": "blockers", "icon": "🚧"},
|
| {"title": "PROCHAINES ÉTAPES", "content_type": "next", "icon": "⏭️"},
|
| {"title": "BESOINS", "content_type": "needs", "icon": "🆘"}
|
| ]
|
| }
|
| }
|
|
|
|
|
| selected_plan = plans.get(category, plans["Rapport d'Étude"])
|
|
|
|
|
| outline = {
|
| "title": metadata['title'].upper(),
|
| "category": category,
|
| "metadata": metadata,
|
| "features": features,
|
| "slides": []
|
| }
|
|
|
|
|
| title_slide = {
|
| "type": "title",
|
| "title": outline["title"],
|
| "subtitle": f"LAB_MATH & LABHP | DIVISION PROSPECTIVE\n{category.upper()}",
|
| "metadata": metadata
|
| }
|
| outline["slides"].append(title_slide)
|
|
|
|
|
| outline["slides"].append({
|
| "type": "document_info",
|
| "title": "INFORMATIONS SUR LE DOCUMENT",
|
| "metadata": metadata,
|
| "features": features
|
| })
|
|
|
|
|
| sentences = nltk.sent_tokenize(text) if nltk else [s for s in text.split('.') if len(s) > 20]
|
|
|
| for slide_config in selected_plan["slides"]:
|
| content = self._extract_content_for_type(sentences, slide_config["content_type"], features)
|
|
|
| outline["slides"].append({
|
| "type": "content",
|
| "icon": slide_config["icon"],
|
| "title": f"{slide_config['icon']} {slide_config['title']}",
|
| "content": content,
|
| "content_type": slide_config["content_type"]
|
| })
|
|
|
|
|
| outline["slides"].append({
|
| "type": "thanks",
|
| "title": "MERCI POUR VOTRE ATTENTION",
|
| "content": [
|
| "Thank you (EN)",
|
| "Gracias (ES)",
|
| "Danke (DE)",
|
| "شكراً (AR)",
|
| "Asante (SW)",
|
| "Merci beaucoup (FR)"
|
| ]
|
| })
|
|
|
| return outline
|
|
|
| def _extract_content_for_type(self, sentences: List[str], content_type: str, features: Dict) -> List[str]:
|
| """Extrait le contenu spécifique selon le type de slide"""
|
| content_patterns = {
|
| "abstract": ["résumé", "abstract", "summary", "synthèse"],
|
| "context": ["contexte", "context", "introduction", "cadre"],
|
| "methods": ["méthodologie", "methodology", "méthode", "approach"],
|
| "results": ["résultat", "result", "finding", "montre"],
|
| "discussion": ["discussion", "analyse", "interpretation"],
|
| "conclusion": ["conclusion", "conclusion"],
|
| "objectives": ["objectif", "objective", "but", "goal"],
|
| "recommendations": ["recommandation", "recommendation", "préconisation"],
|
| "key_message": ["message", "key point", "essentiel"],
|
| "data": ["donnée", "data", "information", "collecte"],
|
| "analysis": ["analyse", "analysis", "étude"],
|
| "findings": ["constat", "finding", "observation"],
|
| "strategic": ["stratégique", "strategic", "axe"],
|
| "agenda": ["ordre du jour", "agenda", "programme"],
|
| "decisions": ["décision", "decision", "validation"],
|
| "actions": ["action", "task", "plan"],
|
| "concepts": ["concept", "notion", "definition"],
|
| "examples": ["exemple", "example", "illustration"],
|
| "exercises": ["exercice", "exercise", "pratique"],
|
| "participants": ["participant", "présent", "attendance"]
|
| }
|
|
|
| keywords = content_patterns.get(content_type, [content_type])
|
|
|
|
|
| relevant = []
|
| for sent in sentences[:50]:
|
| if any(kw in sent.lower() for kw in keywords):
|
|
|
| clean = sent.strip()
|
| if len(clean) > 150:
|
| clean = clean[:147] + "..."
|
| if len(clean) > 20:
|
| relevant.append(clean)
|
|
|
|
|
| if len(relevant) < 3:
|
| generic = [s for s in sentences if len(s) > 50][:5]
|
| relevant.extend(generic[:3-len(relevant)])
|
|
|
|
|
| if content_type == "methods" and features.get('has_methodology'):
|
| relevant.append("✓ Méthodologie détaillée disponible dans le document source")
|
| elif content_type == "results" and features.get('has_results'):
|
| relevant.append("✓ Résultats significatifs présentés dans le document")
|
| elif content_type == "conclusion" and features.get('has_conclusion'):
|
| relevant.append("✓ Conclusion principale détaillée")
|
|
|
| return relevant[:5]
|
|
|
|
|
|
|
|
|
| class PremiumGenerator:
|
| def __init__(self):
|
| self.colors = {
|
| "Article Scientifique": {
|
| "primary": RGBColor(0, 51, 102),
|
| "secondary": RGBColor(204, 153, 0),
|
| "accent": RGBColor(0, 102, 102)
|
| },
|
| "Communication Scientifique": {
|
| "primary": RGBColor(102, 0, 51),
|
| "secondary": RGBColor(255, 153, 0),
|
| "accent": RGBColor(0, 102, 153)
|
| },
|
| "Rapport d'Étude": {
|
| "primary": RGBColor(0, 76, 76),
|
| "secondary": RGBColor(204, 102, 0),
|
| "accent": RGBColor(76, 0, 76)
|
| },
|
| "Rapport de Réunion": {
|
| "primary": RGBColor(51, 51, 102),
|
| "secondary": RGBColor(153, 153, 0),
|
| "accent": RGBColor(102, 51, 102)
|
| },
|
| "Cours pour Étudiants": {
|
| "primary": RGBColor(0, 51, 102),
|
| "secondary": RGBColor(204, 102, 0),
|
| "accent": RGBColor(0, 102, 51)
|
| },
|
| "Note de Synthèse": {
|
| "primary": RGBColor(51, 51, 51),
|
| "secondary": RGBColor(153, 0, 0),
|
| "accent": RGBColor(0, 51, 102)
|
| },
|
| "Document de Travail": {
|
| "primary": RGBColor(102, 51, 0),
|
| "secondary": RGBColor(0, 102, 102),
|
| "accent": RGBColor(153, 0, 76)
|
| }
|
| }
|
| self.default_colors = {
|
| "primary": RGBColor(0, 32, 96),
|
| "secondary": RGBColor(197, 150, 30),
|
| "accent": RGBColor(0, 96, 96)
|
| }
|
|
|
| def get_colors(self, category):
|
| return self.colors.get(category, self.default_colors)
|
|
|
| def generate(self, outline):
|
| prs = Presentation()
|
| prs.slide_width, prs.slide_height = Inches(13.33), Inches(7.5)
|
| colors = self.get_colors(outline["category"])
|
|
|
| for i, slide_data in enumerate(outline["slides"]):
|
| stype = slide_data["type"]
|
|
|
|
|
| if stype == "title":
|
| layout = prs.slide_layouts[0]
|
| elif stype == "document_info":
|
| layout = prs.slide_layouts[1]
|
| else:
|
| layout = prs.slide_layouts[1]
|
|
|
| slide = prs.slides.add_slide(layout)
|
|
|
|
|
| title_bar = slide.shapes.add_shape(
|
| MSO_SHAPE.RECTANGLE, 0, 0, prs.slide_width, Inches(0.15)
|
| )
|
| title_bar.fill.solid()
|
| title_bar.fill.fore_color.rgb = colors["primary"]
|
| title_bar.line.fill.background()
|
|
|
|
|
| title = slide.shapes.title
|
| title.text = slide_data["title"]
|
| title.text_frame.paragraphs[0].font.color.rgb = colors["primary"]
|
| title.text_frame.paragraphs[0].font.bold = True
|
| title.text_frame.paragraphs[0].font.size = Pt(32 if stype != "title" else 40)
|
| title.text_frame.paragraphs[0].alignment = PP_ALIGN.LEFT
|
|
|
| if stype == "title":
|
|
|
| sub = slide.placeholders[1]
|
| sub.text = slide_data["subtitle"]
|
| sub.text_frame.paragraphs[0].font.color.rgb = colors["secondary"]
|
| sub.text_frame.paragraphs[0].font.size = Pt(18)
|
|
|
|
|
| if slide_data.get("metadata"):
|
| metadata_text = f"\nAuteur(s): {', '.join(slide_data['metadata'].get('authors', ['Non spécifié']))}\n"
|
| metadata_text += f"Date: {slide_data['metadata'].get('date', 'Non spécifiée')}"
|
| sub.text += f"\n\n{metadata_text}"
|
|
|
| elif stype == "document_info":
|
|
|
| body = slide.placeholders[1]
|
| tf = body.text_frame
|
| tf.clear()
|
|
|
| metadata = slide_data["metadata"]
|
| features = slide_data["features"]
|
|
|
|
|
| info_points = [
|
| f"📌 Titre: {metadata.get('title', 'Non spécifié')}",
|
| f"👥 Auteurs: {', '.join(metadata.get('authors', ['Non spécifié']))}",
|
| f"📅 Date: {metadata.get('date', 'Non spécifiée')}",
|
| f"🔑 Mots-clés: {', '.join(metadata.get('keywords', ['Non spécifiés']))}",
|
| f"📊 Statistiques: {features.get('word_count', 0)} mots, {features.get('sentence_count', 0)} phrases",
|
| ]
|
|
|
| for point in info_points:
|
| p = tf.add_paragraph()
|
| p.text = point
|
| p.font.size = Pt(18)
|
| p.space_before = Pt(10)
|
| p.font.color.rgb = RGBColor(60, 60, 60)
|
|
|
| else:
|
|
|
| body = slide.placeholders[1]
|
| tf = body.text_frame
|
| tf.clear()
|
| tf.word_wrap = True
|
|
|
| for point in slide_data["content"]:
|
| p = tf.add_paragraph()
|
| p.text = f"• {point}"
|
| p.font.size = Pt(20)
|
| p.space_before = Pt(12)
|
| p.font.color.rgb = RGBColor(60, 60, 60)
|
|
|
|
|
| if "important" in point.lower() or "clé" in point.lower():
|
| p.font.bold = True
|
| p.font.color.rgb = colors["secondary"]
|
|
|
|
|
| if stype != "title":
|
| slide_num = slide.shapes.add_textbox(
|
| Inches(12), Inches(7.0), Inches(1), Inches(0.3)
|
| )
|
| slide_num.text_frame.text = str(i+1)
|
| slide_num.text_frame.paragraphs[0].font.size = Pt(10)
|
| slide_num.text_frame.paragraphs[0].font.color.rgb = colors["accent"]
|
|
|
|
|
| footer = slide.shapes.add_textbox(
|
| Inches(0.5), Inches(7.1), Inches(4), Inches(0.3)
|
| )
|
| footer.text_frame.text = f"LAB_MATH Prospective | {outline['category']}"
|
| footer.text_frame.paragraphs[0].font.size = Pt(8)
|
| footer.text_frame.paragraphs[0].font.italic = True
|
|
|
|
|
| filename = f"Presentation_{outline['category'].replace(' ', '_')}_{int(time.time())}.pptx"
|
| prs.save(filename)
|
| return filename
|
|
|
|
|
|
|
|
|
| ai = SmartAIEnhance()
|
| gen = PremiumGenerator()
|
| ACCESS_KEY = "32015LabMath_1a"
|
| ADMIN_KEY = "admin123"
|
|
|
| def extract_text(file_obj):
|
| """Extrait le texte du fichier uploadé"""
|
| try:
|
| if file_obj.name.endswith('.pdf'):
|
| reader = PyPDF2.PdfReader(file_obj.name)
|
| text = " ".join([p.extract_text() for p in reader.pages if p.extract_text()])
|
| elif file_obj.name.endswith('.docx'):
|
| doc = Document(file_obj.name)
|
| text = " ".join([p.text for p in doc.paragraphs])
|
| elif file_obj.name.endswith('.txt'):
|
| with open(file_obj.name, 'r', encoding='utf-8', errors='ignore') as f:
|
| text = f.read()
|
| else:
|
| text = open(file_obj.name, 'r', encoding='utf-8', errors='ignore').read()
|
|
|
|
|
| text = re.sub(r'\s+', ' ', text)
|
| return text.strip()
|
|
|
| except Exception as e:
|
| raise Exception(f"Erreur lors de l'extraction du texte: {str(e)}")
|
|
|
| def process(file, category, key):
|
| """Fonction principale de traitement"""
|
|
|
| if key != ACCESS_KEY:
|
| return None, "❌ Clé d'accès invalide. Accès refusé."
|
|
|
| if not file:
|
| return None, "⚠️ Veuillez uploader un fichier."
|
|
|
| try:
|
|
|
| text = extract_text(file)
|
|
|
| if len(text) < 100:
|
| return None, "⚠️ Le document est trop court ou n'a pas pu être lu correctement."
|
|
|
|
|
| outline = ai.create_adaptive_outline(text, category)
|
|
|
|
|
| ppt_path = gen.generate(outline)
|
|
|
| return ppt_path, f"""✅ Succès ! Présentation générée avec succès.
|
|
|
| 📊 Type: {category}
|
| 📝 Slides: {len(outline['slides'])}
|
| ⏱️ Temps de traitement: {int(time.time() - time.time())}s
|
|
|
| La présentation est prête à être utilisée sans modifications supplémentaires."""
|
|
|
| except Exception as e:
|
| return None, f"❌ Erreur lors du traitement: {str(e)}"
|
|
|
| def preview_outline(file, category, key):
|
| """Aperçu du plan avant génération"""
|
| if key != ACCESS_KEY:
|
| return "Clé invalide"
|
|
|
| if not file:
|
| return "Veuillez uploader un fichier"
|
|
|
| try:
|
| text = extract_text(file)
|
| outline = ai.create_adaptive_outline(text, category)
|
|
|
| preview = f"📋 APERÇU DU PLAN - {category}\n"
|
| preview += "=" * 50 + "\n\n"
|
|
|
| for i, slide in enumerate(outline["slides"]):
|
| if slide["type"] != "thanks":
|
| preview += f"{i+1}. {slide['title']}\n"
|
| if slide.get("content"):
|
| preview += f" → {len(slide['content'])} points\n"
|
|
|
| preview += f"\nTotal: {len(outline['slides'])} slides"
|
| return preview
|
|
|
| except Exception as e:
|
| return f"Erreur: {str(e)}"
|
|
|
|
|
|
|
|
|
| with gr.Blocks(theme=gr.themes.Soft(), title="Lab_Math Premium Generator") as demo:
|
| gr.Markdown("""
|
| # 🎯 Lab_Math Premium Slide Generator
|
| ### Générateur intelligent de présentations PowerPoint adaptatives
|
| """)
|
|
|
| with gr.Tabs():
|
| with gr.TabItem("📤 GÉNÉRATION"):
|
| with gr.Row():
|
| with gr.Column(scale=1):
|
|
|
| gr.Markdown("### 🔐 Sécurité")
|
| key_in = gr.Textbox(
|
| label="Clé d'accès",
|
| placeholder="Entrez votre clé de déverrouillage...",
|
| type="password"
|
| )
|
|
|
| gr.Markdown("### 📁 Document source")
|
| file_in = gr.File(
|
| label="Uploader votre document",
|
| file_types=[".pdf", ".docx", ".txt"]
|
| )
|
|
|
| gr.Markdown("### 📋 Type de document")
|
| cat_in = gr.Dropdown(
|
| choices=[
|
| "Article Scientifique",
|
| "Communication Scientifique",
|
| "Rapport d'Étude",
|
| "Rapport de Réunion",
|
| "Cours pour Étudiants",
|
| "Note de Synthèse",
|
| "Document de Travail"
|
| ],
|
| label="Sélectionner le type",
|
| value="Rapport d'Étude"
|
| )
|
|
|
| with gr.Row():
|
| preview_btn = gr.Button("👁️ Aperçu du plan", variant="secondary")
|
| generate_btn = gr.Button("✨ GÉNÉRER LA PRÉSENTATION", variant="primary", size="lg")
|
|
|
| with gr.Column(scale=1):
|
|
|
| gr.Markdown("### 📊 Résultats")
|
| file_out = gr.File(label="Télécharger PowerPoint")
|
| status_out = gr.Textbox(
|
| label="Statut",
|
| lines=4,
|
| interactive=False
|
| )
|
|
|
| with gr.Accordion("📋 Aperçu du plan", open=False):
|
| preview_out = gr.Textbox(
|
| label="Structure de la présentation",
|
| lines=10,
|
| interactive=False
|
| )
|
|
|
|
|
| generate_btn.click(
|
| process,
|
| inputs=[file_in, cat_in, key_in],
|
| outputs=[file_out, status_out]
|
| )
|
|
|
| preview_btn.click(
|
| preview_outline,
|
| inputs=[file_in, cat_in, key_in],
|
| outputs=[preview_out]
|
| )
|
|
|
| with gr.TabItem("📖 GUIDE D'UTILISATION"):
|
| gr.Markdown("""
|
| ## Comment utiliser l'outil ?
|
|
|
| ### 1. Sélection du type de document
|
| - **Article Scientifique** : Pour publications académiques, thèses
|
| - **Communication Scientifique** : Présentations orales, posters
|
| - **Rapport d'Étude** : Études de marché, rapports techniques
|
| - **Rapport de Réunion** : Comptes-rendus, synthèses de réunion
|
| - **Cours pour Étudiants** : Supports pédagogiques
|
| - **Note de Synthèse** : Documents de synthèse
|
| - **Document de Travail** : Brouillons, versions de travail
|
|
|
| ### 2. Fonctionnalités intelligentes
|
| - ✓ Extraction automatique du titre
|
| - ✓ Identification des auteurs
|
| - ✓ Détection des sections clés
|
| - ✓ Adaptation du design (couleurs par type)
|
| - ✓ Structure de slides optimisée
|
|
|
| ### 3. Format de sortie
|
| - PowerPoint professionnel prêt à l'emploi
|
| - Design adapté au contexte
|
| - Contenu structuré logiquement
|
| - Aucune retouche nécessaire
|
|
|
| ### 4. Bonnes pratiques
|
| - Documents bien formatés = meilleurs résultats
|
| - Privilégier PDF ou DOCX
|
| - Vérifier le plan avant génération
|
| - Clé d'accès requise pour la sécurité
|
| """)
|
|
|
| with gr.TabItem("ℹ️ À PROPOS"):
|
| gr.Markdown("""
|
| ## Lab_Math Premium Slide Generator
|
|
|
| ### Version 2.0 - Intelligence Adaptative
|
|
|
| **Développé par :** Division Prospective LAB_MATH & LABHP
|
|
|
| ### Caractéristiques techniques
|
| - 🤖 IA adaptative par type de document
|
| - 🎨 Design premium personnalisé
|
| - 🔒 Sécurité renforcée
|
| - 📊 Extraction intelligente des données
|
| - ⚡ Génération ultra-rapide
|
|
|
| ### Contact
|
| - 📧 Email: labmath.prospective@institution.edu
|
| - 🌐 Site: https://labmath-prospective.edu
|
| - 📍 Division Prospective - LAB_MATH
|
|
|
| © 2024 - Tous droits réservés
|
| """)
|
|
|
| if __name__ == "__main__":
|
| demo.launch(server_name="0.0.0.0", server_port=7860) |