pierreguillou commited on
Commit
e91c209
·
verified ·
1 Parent(s): 077986a

Update app.py

Browse files
Files changed (1) hide show
  1. app.py +334 -0
app.py CHANGED
@@ -65,4 +65,338 @@ def extract_text_from_file(file):
65
  return text
66
 
67
  elif file_extension == '.docx':
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
68
  )
 
65
  return text
66
 
67
  elif file_extension == '.docx':
68
+ doc = docx.Document(file.name)
69
+ text = ""
70
+ for paragraph in doc.paragraphs:
71
+ text += paragraph.text + "\n"
72
+ return text
73
+
74
+ elif file_extension == '.txt':
75
+ with open(file.name, 'r', encoding='utf-8') as f:
76
+ return f.read()
77
+
78
+ elif file_extension in ['.xlsx', '.xls']:
79
+ df = pd.read_excel(file.name)
80
+ return df.to_string()
81
+
82
+ elif file_extension == '.pptx':
83
+ prs = Presentation(file.name)
84
+ text = ""
85
+ for slide in prs.slides:
86
+ for shape in slide.shapes:
87
+ if hasattr(shape, "text"):
88
+ text += shape.text + "\n"
89
+ return text
90
+
91
+ else:
92
+ return "Format de fichier non supporté"
93
+
94
+ except Exception as e:
95
+ return f"Erreur lors de la lecture du fichier: {str(e)}"
96
+
97
+ def extract_text_from_url(url):
98
+ """Extrait le texte d'une URL"""
99
+ try:
100
+ response = requests.get(url, timeout=10)
101
+ response.raise_for_status()
102
+
103
+ # Simple extraction du contenu textuel
104
+ content = response.text
105
+ # Suppression basique des balises HTML
106
+ content = re.sub(r'<[^>]+>', '', content)
107
+ content = re.sub(r'\s+', ' ', content).strip()
108
+
109
+ return content[:10000] # Limite à 10k caractères
110
+
111
+ except Exception as e:
112
+ return f"Erreur lors de la récupération de l'URL: {str(e)}"
113
+
114
+ def get_document_content(text_input, url_input, file_input):
115
+ """Récupère le contenu du document selon la source"""
116
+ if text_input.strip():
117
+ return text_input.strip()
118
+ elif url_input.strip():
119
+ return extract_text_from_url(url_input.strip())
120
+ elif file_input is not None:
121
+ return extract_text_from_file(file_input)
122
+ else:
123
+ return ""
124
+
125
+ def create_llm_instance(model_name, api_key):
126
+ """Crée une instance du modèle LLM"""
127
+ model_config = MODELS[model_name]
128
+
129
+ if model_config["provider"] == "OpenAI":
130
+ return model_config["class"](
131
+ model=model_config["model_name"],
132
+ api_key=api_key,
133
+ temperature=0.7
134
+ )
135
+ elif model_config["provider"] == "Anthropic":
136
+ return model_config["class"](
137
+ model=model_config["model_name"],
138
+ api_key=api_key,
139
+ temperature=0.7
140
+ )
141
+ elif model_config["provider"] == "Google AI":
142
+ api_to_use = api_key if api_key else DEFAULT_GEMINI_API
143
+ return model_config["class"](
144
+ model=model_config["model_name"],
145
+ google_api_key=api_to_use,
146
+ temperature=0.7
147
+ )
148
+
149
+ def generate_html(model_name, api_key, text_input, url_input, file_input):
150
+ """Génère le fichier HTML éducatif"""
151
+ start_time = time.time()
152
+
153
+ # Validation des entrées
154
+ if model_name != "Gemini 2.5 Flash (Google AI)" and not api_key.strip():
155
+ return None, "❌ Erreur: Veuillez fournir une clé API pour ce modèle.", 0
156
+
157
+ document_content = get_document_content(text_input, url_input, file_input)
158
+ if not document_content:
159
+ return None, "❌ Erreur: Veuillez fournir un document (texte, URL ou fichier).", 0
160
+
161
+ try:
162
+ # Création de l'instance LLM
163
+ llm = create_llm_instance(model_name, api_key)
164
+
165
+ # Lecture du prompt template
166
+ with open("creation_educational_html_from_any_document_18082025.txt", "r", encoding="utf-8") as f:
167
+ prompt_template = f.read()
168
+
169
+ # Remplacement des variables
170
+ model_config = MODELS[model_name]
171
+ prompt = prompt_template.format(
172
+ model_name=model_config["model_name"],
173
+ provider_name=model_config["provider"],
174
+ document=document_content
175
+ )
176
+
177
+ # Génération du contenu
178
+ message = HumanMessage(content=prompt)
179
+ response = llm.invoke([message])
180
+
181
+ html_content = response.content
182
+
183
+ # Calcul du temps de génération
184
+ generation_time = time.time() - start_time
185
+
186
+ # Sauvegarde du fichier HTML
187
+ timestamp = datetime.now().strftime("%Y%m%d_%H%M%S")
188
+ filename = f"document_educatif_{timestamp}.html"
189
+
190
+ with open(filename, "w", encoding="utf-8") as f:
191
+ f.write(html_content)
192
+
193
+ success_message = f"✅ Fichier HTML généré avec succès en {generation_time:.2f} secondes!"
194
+
195
+ return filename, success_message, generation_time
196
+
197
+ except Exception as e:
198
+ error_message = f"❌ Erreur lors de la génération: {str(e)}"
199
+ return None, error_message, 0
200
+
201
+ def reset_form():
202
+ """Remet à zéro le formulaire"""
203
+ return (
204
+ "Gemini 2.5 Flash (Google AI)", # model_name
205
+ "", # api_key
206
+ "", # text_input
207
+ "", # url_input
208
+ None, # file_input
209
+ "", # status_message
210
+ None, # html_file
211
+ "" # html_preview
212
+ )
213
+
214
+ def update_api_info(model_name):
215
+ """Met à jour les informations sur l'API selon le modèle sélectionné"""
216
+ if model_name == "Gemini 2.5 Flash (Google AI)":
217
+ return gr.update(
218
+ label="Clé API (optionnelle)",
219
+ placeholder="API gratuite disponible jusqu'à épuisement, ou utilisez votre propre clé",
220
+ info="💡 Une API gratuite est déjà configurée pour ce modèle. Vous pouvez utiliser votre propre clé si vous le souhaitez."
221
+ )
222
+ else:
223
+ return gr.update(
224
+ label="Clé API (obligatoire)",
225
+ placeholder="Entrez votre clé API",
226
+ info="🔑 Clé API requise pour ce modèle"
227
+ )
228
+
229
+ # Interface Gradio
230
+ with gr.Blocks(
231
+ title="EduHTML Creator - Générateur de contenu éducatif HTML",
232
+ theme=gr.themes.Soft(),
233
+ css="""
234
+ .main-container {
235
+ max-width: 1200px;
236
+ margin: 0 auto;
237
+ padding: 20px;
238
+ }
239
+ .header {
240
+ text-align: center;
241
+ margin-bottom: 30px;
242
+ padding: 30px;
243
+ background: linear-gradient(135deg, #667eea 0%, #764ba2 100%);
244
+ border-radius: 15px;
245
+ color: white;
246
+ }
247
+ .form-section {
248
+ background: white;
249
+ padding: 25px;
250
+ border-radius: 15px;
251
+ box-shadow: 0 4px 6px rgba(0, 0, 0, 0.1);
252
+ margin-bottom: 20px;
253
+ }
254
+ .apple-button {
255
+ background: #007AFF;
256
+ color: white;
257
+ border: none;
258
+ border-radius: 8px;
259
+ padding: 12px 24px;
260
+ font-weight: 500;
261
+ transition: all 0.3s ease;
262
+ }
263
+ .apple-button:hover {
264
+ background: #0056CC;
265
+ transform: translateY(-1px);
266
+ }
267
+ .reset-button {
268
+ background: #FF3B30;
269
+ color: white;
270
+ border: none;
271
+ border-radius: 8px;
272
+ padding: 12px 24px;
273
+ font-weight: 500;
274
+ }
275
+ .status-success {
276
+ color: #34C759;
277
+ font-weight: 500;
278
+ }
279
+ .status-error {
280
+ color: #FF3B30;
281
+ font-weight: 500;
282
+ }
283
+ """
284
+ ) as app:
285
+
286
+ gr.HTML("""
287
+ <div class="header">
288
+ <h1>🎓 EduHTML Creator</h1>
289
+ <p style="font-size: 18px; margin: 10px 0;">Transformez n'importe quel document en contenu éducatif HTML interactif</p>
290
+ <p style="font-size: 14px; opacity: 0.9;">
291
+ Cette application utilise l'intelligence artificielle pour créer des pages HTML éducatives élégantes et interactives
292
+ à partir de vos documents. Le design s'inspire du style Apple pour une expérience utilisateur premium.
293
+ L'objectif éducatif est de faciliter l'apprentissage grâce à la structuration, l'interactivité et la visualisation
294
+ des informations clés de vos documents originaux.
295
+ </p>
296
+ </div>
297
+ """)
298
+
299
+ with gr.Row():
300
+ with gr.Column(scale=1):
301
+ gr.HTML("<div class='form-section'>")
302
+
303
+ # Sélection du modèle
304
+ model_dropdown = gr.Dropdown(
305
+ choices=list(MODELS.keys()),
306
+ value="Gemini 2.5 Flash (Google AI)",
307
+ label="🤖 Modèle LLM",
308
+ info="Choisissez le modèle d'IA à utiliser"
309
+ )
310
+
311
+ # Champ API
312
+ api_input = gr.Textbox(
313
+ label="Clé API (optionnelle)",
314
+ placeholder="API gratuite disponible jusqu'à épuisement, ou utilisez votre propre clé",
315
+ info="💡 Une API gratuite est déjà configurée pour ce modèle. Vous pouvez utiliser votre propre clé si vous le souhaitez.",
316
+ type="password"
317
+ )
318
+
319
+ gr.HTML("</div>")
320
+
321
+ gr.HTML("<div class='form-section'>")
322
+ gr.HTML("<h3>📄 Source du document</h3>")
323
+
324
+ # Entrées de document
325
+ text_input = gr.Textbox(
326
+ label="Texte copié/collé",
327
+ placeholder="Collez votre texte ici...",
328
+ lines=5
329
+ )
330
+
331
+ url_input = gr.Textbox(
332
+ label="Lien Web",
333
+ placeholder="https://exemple.com/article"
334
+ )
335
+
336
+ file_input = gr.File(
337
+ label="Fichier",
338
+ file_types=[".pdf", ".txt", ".docx", ".xlsx", ".xls", ".pptx"]
339
+ )
340
+
341
+ gr.HTML("</div>")
342
+
343
+ # Boutons
344
+ with gr.Row():
345
+ submit_btn = gr.Button(
346
+ "🚀 Générer le HTML",
347
+ variant="primary",
348
+ elem_classes=["apple-button"]
349
+ )
350
+ reset_btn = gr.Button(
351
+ "🔄 Reset",
352
+ elem_classes=["reset-button"]
353
+ )
354
+
355
+ with gr.Column(scale=1):
356
+ # Statut et résultats
357
+ status_output = gr.HTML(label="Statut")
358
+
359
+ # Fichier téléchargeable
360
+ html_file_output = gr.File(
361
+ label="📥 Fichier HTML téléchargeable",
362
+ visible=False
363
+ )
364
+
365
+ # Prévisualisation
366
+ html_preview = gr.HTML(
367
+ label="👀 Prévisualisation",
368
+ visible=False
369
+ )
370
+
371
+ # Événements
372
+ model_dropdown.change(
373
+ fn=update_api_info,
374
+ inputs=[model_dropdown],
375
+ outputs=[api_input]
376
+ )
377
+
378
+ submit_btn.click(
379
+ fn=generate_html,
380
+ inputs=[model_dropdown, api_input, text_input, url_input, file_input],
381
+ outputs=[html_file_output, status_output, gr.State()]
382
+ ).then(
383
+ fn=lambda file, status, time_taken: (
384
+ gr.update(visible=file is not None),
385
+ status,
386
+ gr.update(visible=file is not None, value=open(file, 'r', encoding='utf-8').read() if file else "")
387
+ ),
388
+ inputs=[html_file_output, status_output, gr.State()],
389
+ outputs=[html_file_output, status_output, html_preview]
390
+ )
391
+
392
+ reset_btn.click(
393
+ fn=reset_form,
394
+ outputs=[model_dropdown, api_input, text_input, url_input, file_input, status_output, html_file_output, html_preview]
395
+ )
396
+
397
+ if __name__ == "__main__":
398
+ app.launch(
399
+ server_name="0.0.0.0",
400
+ server_port=7860,
401
+ share=True
402
  )