|
import gradio as gr |
|
import os |
|
import time |
|
import requests |
|
from datetime import datetime |
|
from langchain_openai import ChatOpenAI |
|
from langchain_anthropic import ChatAnthropic |
|
from langchain_google_genai import ChatGoogleGenerativeAI |
|
from langchain_core.messages import HumanMessage |
|
import PyPDF2 |
|
import docx |
|
import pandas as pd |
|
from pptx import Presentation |
|
import io |
|
import tempfile |
|
from urllib.parse import urlparse |
|
import re |
|
|
|
|
|
MODELS = { |
|
"Gemini 2.5 Flash (Google AI)": { |
|
"provider": "Google AI", |
|
"class": ChatGoogleGenerativeAI, |
|
"model_name": "gemini-2.0-flash-exp", |
|
"default_api": True |
|
}, |
|
"ChatGPT 5 (OpenAI)": { |
|
"provider": "OpenAI", |
|
"class": ChatOpenAI, |
|
"model_name": "gpt-4o", |
|
"default_api": False |
|
}, |
|
"Claude Sonnet 4 (Anthropic)": { |
|
"provider": "Anthropic", |
|
"class": ChatAnthropic, |
|
"model_name": "claude-3-5-sonnet-20241022", |
|
"default_api": False |
|
}, |
|
"Gemini 2.5 Pro (Google AI)": { |
|
"provider": "Google AI", |
|
"class": ChatGoogleGenerativeAI, |
|
"model_name": "gemini-2.0-flash-exp", |
|
"default_api": False |
|
} |
|
} |
|
|
|
|
|
DEFAULT_GEMINI_API = os.getenv("GOOGLE_API_KEY", "") |
|
|
|
def extract_text_from_file(file): |
|
"""Extrait le texte d'un fichier uploadé""" |
|
if file is None: |
|
return "" |
|
|
|
file_extension = os.path.splitext(file.name)[1].lower() |
|
|
|
try: |
|
if file_extension == '.pdf': |
|
with open(file.name, 'rb') as f: |
|
reader = PyPDF2.PdfReader(f) |
|
text = "" |
|
for page in reader.pages: |
|
text += page.extract_text() + "\n" |
|
return text |
|
|
|
elif file_extension == '.docx': |
|
doc = docx.Document(file.name) |
|
text = "" |
|
for paragraph in doc.paragraphs: |
|
text += paragraph.text + "\n" |
|
return text |
|
|
|
elif file_extension == '.txt': |
|
with open(file.name, 'r', encoding='utf-8') as f: |
|
return f.read() |
|
|
|
elif file_extension in ['.xlsx', '.xls']: |
|
df = pd.read_excel(file.name) |
|
return df.to_string() |
|
|
|
elif file_extension == '.pptx': |
|
prs = Presentation(file.name) |
|
text = "" |
|
for slide in prs.slides: |
|
for shape in slide.shapes: |
|
if hasattr(shape, "text"): |
|
text += shape.text + "\n" |
|
return text |
|
|
|
else: |
|
return "Format de fichier non supporté" |
|
|
|
except Exception as e: |
|
return f"Erreur lors de la lecture du fichier: {str(e)}" |
|
|
|
def extract_text_from_url(url): |
|
"""Extrait le texte d'une URL""" |
|
try: |
|
response = requests.get(url, timeout=10) |
|
response.raise_for_status() |
|
|
|
|
|
content = response.text |
|
|
|
content = re.sub(r'<[^>]+>', '', content) |
|
content = re.sub(r'\s+', ' ', content).strip() |
|
|
|
return content[:10000] |
|
|
|
except Exception as e: |
|
return f"Erreur lors de la récupération de l'URL: {str(e)}" |
|
|
|
def get_document_content(text_input, url_input, file_input): |
|
"""Récupère le contenu du document selon la source""" |
|
if text_input.strip(): |
|
return text_input.strip() |
|
elif url_input.strip(): |
|
return extract_text_from_url(url_input.strip()) |
|
elif file_input is not None: |
|
return extract_text_from_file(file_input) |
|
else: |
|
return "" |
|
|
|
def create_llm_instance(model_name, api_key): |
|
"""Crée une instance du modèle LLM""" |
|
model_config = MODELS[model_name] |
|
|
|
if model_config["provider"] == "OpenAI": |
|
return model_config["class"]( |
|
model=model_config["model_name"], |
|
api_key=api_key, |
|
temperature=0.7 |
|
) |
|
elif model_config["provider"] == "Anthropic": |
|
return model_config["class"]( |
|
model=model_config["model_name"], |
|
api_key=api_key, |
|
temperature=0.7 |
|
) |
|
elif model_config["provider"] == "Google AI": |
|
api_to_use = api_key if api_key else DEFAULT_GEMINI_API |
|
return model_config["class"]( |
|
model=model_config["model_name"], |
|
google_api_key=api_to_use, |
|
temperature=0.7 |
|
) |
|
|
|
def generate_html(model_name, api_key, text_input, url_input, file_input): |
|
"""Génère le fichier HTML éducatif""" |
|
start_time = time.time() |
|
|
|
|
|
if model_name != "Gemini 2.5 Flash (Google AI)" and not api_key.strip(): |
|
return None, "❌ Erreur: Veuillez fournir une clé API pour ce modèle.", 0 |
|
|
|
document_content = get_document_content(text_input, url_input, file_input) |
|
if not document_content: |
|
return None, "❌ Erreur: Veuillez fournir un document (texte, URL ou fichier).", 0 |
|
|
|
try: |
|
|
|
llm = create_llm_instance(model_name, api_key) |
|
|
|
|
|
with open("creation_educational_html_from_any_document_18082025.txt", "r", encoding="utf-8") as f: |
|
prompt_template = f.read() |
|
|
|
|
|
model_config = MODELS[model_name] |
|
prompt = prompt_template.format( |
|
model_name=model_config["model_name"], |
|
provider_name=model_config["provider"], |
|
document=document_content |
|
) |
|
|
|
|
|
message = HumanMessage(content=prompt) |
|
response = llm.invoke([message]) |
|
|
|
html_content = response.content |
|
|
|
|
|
generation_time = time.time() - start_time |
|
|
|
|
|
timestamp = datetime.now().strftime("%Y%m%d_%H%M%S") |
|
filename = f"document_educatif_{timestamp}.html" |
|
|
|
with open(filename, "w", encoding="utf-8") as f: |
|
f.write(html_content) |
|
|
|
success_message = f"✅ Fichier HTML généré avec succès en {generation_time:.2f} secondes!" |
|
|
|
return filename, success_message, generation_time |
|
|
|
except Exception as e: |
|
error_message = f"❌ Erreur lors de la génération: {str(e)}" |
|
return None, error_message, 0 |
|
|
|
def reset_form(): |
|
"""Remet à zéro le formulaire""" |
|
return ( |
|
"Gemini 2.5 Flash (Google AI)", |
|
"", |
|
"", |
|
"", |
|
None, |
|
"", |
|
None, |
|
"" |
|
) |
|
|
|
def update_api_info(model_name): |
|
"""Met à jour les informations sur l'API selon le modèle sélectionné""" |
|
if model_name == "Gemini 2.5 Flash (Google AI)": |
|
return gr.update( |
|
label="Clé API (optionnelle)", |
|
placeholder="API gratuite disponible jusqu'à épuisement, ou utilisez votre propre clé", |
|
info="💡 Une API gratuite est déjà configurée pour ce modèle. Vous pouvez utiliser votre propre clé si vous le souhaitez." |
|
) |
|
else: |
|
return gr.update( |
|
label="Clé API (obligatoire)", |
|
placeholder="Entrez votre clé API", |
|
info="🔑 Clé API requise pour ce modèle" |
|
) |
|
|
|
|
|
with gr.Blocks( |
|
title="EduHTML Creator - Générateur de contenu éducatif HTML", |
|
theme=gr.themes.Soft(), |
|
css=""" |
|
.main-container { |
|
max-width: 1200px; |
|
margin: 0 auto; |
|
padding: 20px; |
|
} |
|
.header { |
|
text-align: center; |
|
margin-bottom: 30px; |
|
padding: 30px; |
|
background: linear-gradient(135deg, #667eea 0%, #764ba2 100%); |
|
border-radius: 15px; |
|
color: white; |
|
} |
|
.form-section { |
|
background: white; |
|
padding: 25px; |
|
border-radius: 15px; |
|
box-shadow: 0 4px 6px rgba(0, 0, 0, 0.1); |
|
margin-bottom: 20px; |
|
} |
|
.apple-button { |
|
background: #007AFF; |
|
color: white; |
|
border: none; |
|
border-radius: 8px; |
|
padding: 12px 24px; |
|
font-weight: 500; |
|
transition: all 0.3s ease; |
|
} |
|
.apple-button:hover { |
|
background: #0056CC; |
|
transform: translateY(-1px); |
|
} |
|
.reset-button { |
|
background: #FF3B30; |
|
color: white; |
|
border: none; |
|
border-radius: 8px; |
|
padding: 12px 24px; |
|
font-weight: 500; |
|
} |
|
.status-success { |
|
color: #34C759; |
|
font-weight: 500; |
|
} |
|
.status-error { |
|
color: #FF3B30; |
|
font-weight: 500; |
|
} |
|
""" |
|
) as app: |
|
|
|
gr.HTML(""" |
|
<div class="header"> |
|
<h1>🎓 EduHTML Creator</h1> |
|
<p style="font-size: 18px; margin: 10px 0;">Transformez n'importe quel document en contenu éducatif HTML interactif</p> |
|
<p style="font-size: 14px; opacity: 0.9;"> |
|
Cette application utilise l'intelligence artificielle pour créer des pages HTML éducatives élégantes et interactives |
|
à partir de vos documents. Le design s'inspire du style Apple pour une expérience utilisateur premium. |
|
L'objectif éducatif est de faciliter l'apprentissage grâce à la structuration, l'interactivité et la visualisation |
|
des informations clés de vos documents originaux. |
|
</p> |
|
</div> |
|
""") |
|
|
|
with gr.Row(): |
|
with gr.Column(scale=1): |
|
gr.HTML("<div class='form-section'>") |
|
|
|
|
|
model_dropdown = gr.Dropdown( |
|
choices=list(MODELS.keys()), |
|
value="Gemini 2.5 Flash (Google AI)", |
|
label="🤖 Modèle LLM", |
|
info="Choisissez le modèle d'IA à utiliser" |
|
) |
|
|
|
|
|
api_input = gr.Textbox( |
|
label="Clé API (optionnelle)", |
|
placeholder="API gratuite disponible jusqu'à épuisement, ou utilisez votre propre clé", |
|
info="💡 Une API gratuite est déjà configurée pour ce modèle. Vous pouvez utiliser votre propre clé si vous le souhaitez.", |
|
type="password" |
|
) |
|
|
|
gr.HTML("</div>") |
|
|
|
gr.HTML("<div class='form-section'>") |
|
gr.HTML("<h3>📄 Source du document</h3>") |
|
|
|
|
|
text_input = gr.Textbox( |
|
label="Texte copié/collé", |
|
placeholder="Collez votre texte ici...", |
|
lines=5 |
|
) |
|
|
|
url_input = gr.Textbox( |
|
label="Lien Web", |
|
placeholder="https://exemple.com/article" |
|
) |
|
|
|
file_input = gr.File( |
|
label="Fichier", |
|
file_types=[".pdf", ".txt", ".docx", ".xlsx", ".xls", ".pptx"] |
|
) |
|
|
|
gr.HTML("</div>") |
|
|
|
|
|
with gr.Row(): |
|
submit_btn = gr.Button( |
|
"🚀 Générer le HTML", |
|
variant="primary", |
|
elem_classes=["apple-button"] |
|
) |
|
reset_btn = gr.Button( |
|
"🔄 Reset", |
|
elem_classes=["reset-button"] |
|
) |
|
|
|
with gr.Column(scale=1): |
|
|
|
status_output = gr.HTML(label="Statut") |
|
|
|
|
|
html_file_output = gr.File( |
|
label="📥 Fichier HTML téléchargeable", |
|
visible=False |
|
) |
|
|
|
|
|
html_preview = gr.HTML( |
|
label="👀 Prévisualisation", |
|
visible=False |
|
) |
|
|
|
|
|
model_dropdown.change( |
|
fn=update_api_info, |
|
inputs=[model_dropdown], |
|
outputs=[api_input] |
|
) |
|
|
|
submit_btn.click( |
|
fn=generate_html, |
|
inputs=[model_dropdown, api_input, text_input, url_input, file_input], |
|
outputs=[html_file_output, status_output, gr.State()] |
|
).then( |
|
fn=lambda file, status, time_taken: ( |
|
gr.update(visible=file is not None), |
|
status, |
|
gr.update(visible=file is not None, value=open(file, 'r', encoding='utf-8').read() if file else "") |
|
), |
|
inputs=[html_file_output, status_output, gr.State()], |
|
outputs=[html_file_output, status_output, html_preview] |
|
) |
|
|
|
reset_btn.click( |
|
fn=reset_form, |
|
outputs=[model_dropdown, api_input, text_input, url_input, file_input, status_output, html_file_output, html_preview] |
|
) |
|
|
|
if __name__ == "__main__": |
|
app.launch( |
|
server_name="0.0.0.0", |
|
server_port=7860, |
|
share=True |
|
) |