Docfile commited on
Commit
e0b7ad0
·
verified ·
1 Parent(s): 5d8be6a

Update app.py

Browse files
Files changed (1) hide show
  1. app.py +157 -25
app.py CHANGED
@@ -11,16 +11,15 @@ import tempfile
11
  import subprocess
12
  import shutil
13
  import re
 
14
  from flask import Flask, render_template, request, jsonify, Response, stream_with_context, send_from_directory
15
  from google import genai
16
  from google.genai import types
17
  from PIL import Image
18
 
19
- # --- Configuraticon du Logging ---
20
- # Configuration d'un logger qui écrit dans la console (stdout).
21
- # C'est la pratique recommandée pour les applications conteneurisées (Docker) ou déployées sur des services comme Heroku/Render.
22
  logging.basicConfig(
23
- level=logging.INFO, # Niveau de log par défaut. Changer à logging.DEBUG pour plus de détails.
24
  format='%(asctime)s - %(name)s - %(levelname)s - %(message)s',
25
  datefmt='%Y-%m-%d %H:%M:%S'
26
  )
@@ -34,6 +33,7 @@ GOOGLE_API_KEY = os.environ.get("GOOGLE_API_KEY")
34
  TELEGRAM_BOT_TOKEN = "8004545342:AAGcZaoDjYg8dmbbXRsR1N3TfSSbEiAGz88"
35
  TELEGRAM_CHAT_ID = "-1002564204301"
36
  GENERATED_PDF_DIR = os.path.join(os.path.dirname(os.path.abspath(__file__)), 'generated_pdfs')
 
37
 
38
  # --- Initialisation des Services Externes ---
39
  client = None
@@ -71,8 +71,6 @@ def check_latex_installation():
71
  """Vérifie si pdflatex est installé et accessible dans le PATH."""
72
  logger.info("Vérification de l'installation de LaTeX (pdflatex)...")
73
  try:
74
- # Exécute 'pdflatex -version' pour vérifier son existence.
75
- # capture_output=True masque la sortie, check=True lève une exception en cas d'échec.
76
  subprocess.run(["pdflatex", "-version"], capture_output=True, check=True, timeout=10)
77
  logger.info("Vérification réussie: pdflatex est installé et fonctionnel.")
78
  return True
@@ -82,16 +80,89 @@ def check_latex_installation():
82
 
83
  IS_LATEX_INSTALLED = check_latex_installation()
84
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
85
  def clean_latex_code(latex_code):
86
  """Extrait le code LaTeX brut des blocs de code formatés (```latex ... ```)."""
87
  logger.info("Nettoyage du code LaTeX reçu de Gemini...")
88
- # Cherche un bloc de code explicite ```latex ... ```
89
  match_latex = re.search(r"```(?:latex|tex)\s*(.*?)\s*```", latex_code, re.DOTALL | re.IGNORECASE)
90
  if match_latex:
91
  logger.info("Bloc de code 'latex' ou 'tex' trouvé et extrait.")
92
  return match_latex.group(1).strip()
93
 
94
- # Plan B : Cherche un bloc de code générique qui commence par \documentclass
95
  match_generic = re.search(r"```\s*(\\documentclass.*?)\s*```", latex_code, re.DOTALL | re.IGNORECASE)
96
  if match_generic:
97
  logger.info("Bloc de code générique avec '\\documentclass' trouvé et extrait.")
@@ -113,18 +184,15 @@ def latex_to_pdf(latex_code, output_filename_base, output_dir):
113
  logger.info(f"Début de la compilation LaTeX vers PDF pour '{output_filename_base}'")
114
 
115
  try:
116
- # Écriture du fichier .tex
117
  with open(tex_path, "w", encoding="utf-8") as tex_file:
118
  tex_file.write(latex_code)
119
  logger.info(f"Fichier .tex '{tex_path}' créé avec succès.")
120
 
121
- # Copie de l'environnement et configuration pour UTF-8 pour éviter les erreurs d'encodage
122
  my_env = os.environ.copy()
123
  my_env["LC_ALL"] = "C.UTF-8"
124
  my_env["LANG"] = "C.UTF-8"
125
 
126
  last_result = None
127
- # Exécution de pdflatex deux fois pour résoudre les références (table des matières, etc.)
128
  for i in range(2):
129
  logger.info(f"Exécution de pdflatex - Passe {i+1}/2...")
130
  process = subprocess.run(
@@ -132,7 +200,6 @@ def latex_to_pdf(latex_code, output_filename_base, output_dir):
132
  capture_output=True, text=True, check=False, encoding="utf-8", errors="replace", env=my_env, timeout=60
133
  )
134
  last_result = process
135
- # Si le PDF n'est pas créé et que la première passe a échoué, inutile de continuer
136
  if not os.path.exists(pdf_path) and process.returncode != 0:
137
  logger.warning(f"La passe {i+1} de pdflatex a échoué et aucun PDF n'a été créé. Arrêt de la compilation.")
138
  break
@@ -143,7 +210,7 @@ def latex_to_pdf(latex_code, output_filename_base, output_dir):
143
  else:
144
  error_log = last_result.stdout + "\n" + last_result.stderr if last_result else "Aucun résultat de compilation disponible."
145
  logger.error(f"Échec de la compilation PDF pour '{tex_filename}'. Log de pdflatex:\n{error_log}")
146
- return None, f"Erreur de compilation PDF. Log: ...{error_log[-1000:]}" # Retourne les 1000 derniers caractères du log
147
 
148
  except Exception as e:
149
  logger.error(f"Exception pendant la génération du PDF: {e}", exc_info=True)
@@ -165,12 +232,11 @@ def send_to_telegram(file_data, filename, caption="Nouveau fichier uploadé"):
165
  logger.info(log_msg)
166
  data = {'chat_id': TELEGRAM_CHAT_ID, 'caption': caption}
167
  response = requests.post(url, files=files, data=data, timeout=30)
168
- response.raise_for_status() # Lève une exception si le statut HTTP est une erreur (4xx ou 5xx)
169
  logger.info(f"Fichier '{filename}' envoyé avec succès à Telegram.")
170
  except Exception as e:
171
  logger.error(f"Erreur lors de l'envoi à Telegram: {e}", exc_info=True)
172
 
173
-
174
  # --- Logique Principale (Worker en arrière-plan) ---
175
 
176
  def process_files_background(task_id, files_data, resolution_style):
@@ -188,9 +254,13 @@ def process_files_background(task_id, files_data, resolution_style):
188
  for file_info in files_data:
189
  if file_info['type'].startswith('image/'):
190
  logger.info(f"[Task {task_id}] Traitement de l'image '{file_info['filename']}'.")
 
 
 
 
191
  img = Image.open(io.BytesIO(file_info['data']))
192
  buffered = io.BytesIO()
193
- img.save(buffered, format="PNG") # Convertit en PNG pour la consistance
194
  img_base64_str = base64.b64encode(buffered.getvalue()).decode()
195
  contents.append({'inline_data': {'mime_type': 'image/png', 'data': img_base64_str}})
196
 
@@ -200,11 +270,10 @@ def process_files_background(task_id, files_data, resolution_style):
200
  temp_pdf.write(file_info['data'])
201
  temp_pdf_path = temp_pdf.name
202
 
203
- # Upload du fichier et ajout de la référence à la liste de nettoyage
204
  file_ref = client.files.upload(file=temp_pdf_path)
205
  uploaded_file_refs.append(file_ref)
206
  contents.append(file_ref)
207
- os.unlink(temp_pdf_path) # Supprime le fichier temporaire local
208
  logger.info(f"[Task {task_id}] PDF '{file_info['filename']}' uploadé avec succès. Référence: {file_ref.name}")
209
 
210
  if not contents:
@@ -238,7 +307,6 @@ def process_files_background(task_id, files_data, resolution_style):
238
  cleaned_latex = clean_latex_code(full_latex_response)
239
  logger.debug(f"[Task {task_id}] Code LaTeX nettoyé:\n---\n{cleaned_latex[:500]}...\n---")
240
 
241
-
242
  task_results[task_id]['status'] = 'generating_pdf'
243
  pdf_filename_base = f"solution_{task_id}"
244
  pdf_file_path, pdf_message = latex_to_pdf(cleaned_latex, pdf_filename_base, GENERATED_PDF_DIR)
@@ -257,7 +325,6 @@ def process_files_background(task_id, files_data, resolution_style):
257
  task_results[task_id]['error'] = str(e)
258
  task_results[task_id]['response'] = f"Une erreur est survenue: {str(e)}"
259
  finally:
260
- # Nettoyage des fichiers uploadés à l'API Gemini
261
  if uploaded_file_refs:
262
  logger.info(f"[Task {task_id}] Nettoyage des {len(uploaded_file_refs)} fichiers temporaires de l'API Gemini.")
263
  for file_ref in uploaded_file_refs:
@@ -274,6 +341,71 @@ def index():
274
  logger.info(f"Requête servie pour l'endpoint '/' depuis {request.remote_addr}")
275
  return render_template('index.html')
276
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
277
  @app.route('/solve', methods=['POST'])
278
  def solve():
279
  logger.info(f"Nouvelle requête sur /solve depuis {request.remote_addr}")
@@ -296,7 +428,6 @@ def solve():
296
  file_data = file.read()
297
  file_type = file.content_type or 'application/octet-stream'
298
 
299
- # Validation et traitement des fichiers
300
  if file_type.startswith('image/'):
301
  file_count['images'] += 1
302
  files_data.append({'filename': file.filename, 'data': file_data, 'type': file_type})
@@ -374,7 +505,7 @@ def stream_task_progress(task_id):
374
  logger.info(f"Fermeture de la connexion SSE pour la tâche terminée/échouée {task_id}")
375
  break
376
 
377
- time.sleep(1) # Attendre 1 seconde avant de vérifier à nouveau
378
 
379
  return Response(stream_with_context(generate()), mimetype='text/event-stream', headers={'Cache-Control': 'no-cache', 'X-Accel-Buffering': 'no'})
380
 
@@ -396,8 +527,9 @@ def download_pdf(task_id):
396
  if __name__ == '__main__':
397
  logger.info("Démarrage de l'application Flask.")
398
 
399
- # Création du répertoire pour les PDFs générés s'il n'existe pas
400
  os.makedirs(GENERATED_PDF_DIR, exist_ok=True)
401
- logger.info(f"Répertoire pour les PDFs générés assuré d'exister: '{GENERATED_PDF_DIR}'")
 
402
 
403
- # Vérifications cri
 
11
  import subprocess
12
  import shutil
13
  import re
14
+ from datetime import datetime
15
  from flask import Flask, render_template, request, jsonify, Response, stream_with_context, send_from_directory
16
  from google import genai
17
  from google.genai import types
18
  from PIL import Image
19
 
20
+ # --- Configuration du Logging ---
 
 
21
  logging.basicConfig(
22
+ level=logging.INFO,
23
  format='%(asctime)s - %(name)s - %(levelname)s - %(message)s',
24
  datefmt='%Y-%m-%d %H:%M:%S'
25
  )
 
33
  TELEGRAM_BOT_TOKEN = "8004545342:AAGcZaoDjYg8dmbbXRsR1N3TfSSbEiAGz88"
34
  TELEGRAM_CHAT_ID = "-1002564204301"
35
  GENERATED_PDF_DIR = os.path.join(os.path.dirname(os.path.abspath(__file__)), 'generated_pdfs')
36
+ USER_IMAGES_DIR = os.path.join(os.path.dirname(os.path.abspath(__file__)), 'user_images')
37
 
38
  # --- Initialisation des Services Externes ---
39
  client = None
 
71
  """Vérifie si pdflatex est installé et accessible dans le PATH."""
72
  logger.info("Vérification de l'installation de LaTeX (pdflatex)...")
73
  try:
 
 
74
  subprocess.run(["pdflatex", "-version"], capture_output=True, check=True, timeout=10)
75
  logger.info("Vérification réussie: pdflatex est installé et fonctionnel.")
76
  return True
 
80
 
81
  IS_LATEX_INSTALLED = check_latex_installation()
82
 
83
+ def save_user_image(image_data, filename, task_id):
84
+ """Sauvegarde une image utilisateur dans le dossier user_images."""
85
+ try:
86
+ # Créer un nom de fichier unique avec le task_id
87
+ file_extension = os.path.splitext(filename)[1]
88
+ safe_filename = f"{task_id}_{filename}"
89
+ image_path = os.path.join(USER_IMAGES_DIR, safe_filename)
90
+
91
+ with open(image_path, 'wb') as f:
92
+ f.write(image_data)
93
+
94
+ logger.info(f"Image utilisateur sauvegardée: {safe_filename}")
95
+ return safe_filename
96
+ except Exception as e:
97
+ logger.error(f"Erreur lors de la sauvegarde de l'image {filename}: {e}")
98
+ return None
99
+
100
+ def get_all_tasks_info():
101
+ """Récupère toutes les informations des tâches pour le centre de gestion."""
102
+ tasks_info = []
103
+
104
+ for task_id, task_data in task_results.items():
105
+ task_info = {
106
+ 'id': task_id,
107
+ 'status': task_data['status'],
108
+ 'style': task_data.get('style', 'unknown'),
109
+ 'first_filename': task_data.get('first_filename', 'Unknown'),
110
+ 'time_started': task_data.get('time_started', 0),
111
+ 'time_started_formatted': datetime.fromtimestamp(task_data.get('time_started', 0)).strftime('%Y-%m-%d %H:%M:%S'),
112
+ 'file_count': task_data.get('file_count', {'images': 0, 'pdfs': 0}),
113
+ 'pdf_filename': task_data.get('pdf_filename'),
114
+ 'error': task_data.get('error'),
115
+ 'response': task_data.get('response', ''),
116
+ 'user_images': []
117
+ }
118
+
119
+ # Chercher les images utilisateur associées à cette tâche
120
+ if os.path.exists(USER_IMAGES_DIR):
121
+ for img_file in os.listdir(USER_IMAGES_DIR):
122
+ if img_file.startswith(f"{task_id}_"):
123
+ task_info['user_images'].append(img_file)
124
+
125
+ tasks_info.append(task_info)
126
+
127
+ # Trier par date de création (plus récent en premier)
128
+ tasks_info.sort(key=lambda x: x['time_started'], reverse=True)
129
+ return tasks_info
130
+
131
+ def get_system_stats():
132
+ """Récupère les statistiques du système."""
133
+ total_tasks = len(task_results)
134
+ completed_tasks = sum(1 for task in task_results.values() if task['status'] == 'completed')
135
+ error_tasks = sum(1 for task in task_results.values() if task['status'] == 'error')
136
+ pending_tasks = sum(1 for task in task_results.values() if task['status'] not in ['completed', 'error'])
137
+
138
+ # Compter les fichiers PDF générés
139
+ pdf_files = 0
140
+ if os.path.exists(GENERATED_PDF_DIR):
141
+ pdf_files = len([f for f in os.listdir(GENERATED_PDF_DIR) if f.endswith('.pdf')])
142
+
143
+ # Compter les images utilisateur
144
+ user_images = 0
145
+ if os.path.exists(USER_IMAGES_DIR):
146
+ user_images = len([f for f in os.listdir(USER_IMAGES_DIR) if f.lower().endswith(('.png', '.jpg', '.jpeg', '.gif', '.webp'))])
147
+
148
+ return {
149
+ 'total_tasks': total_tasks,
150
+ 'completed_tasks': completed_tasks,
151
+ 'error_tasks': error_tasks,
152
+ 'pending_tasks': pending_tasks,
153
+ 'pdf_files': pdf_files,
154
+ 'user_images': user_images,
155
+ 'latex_installed': IS_LATEX_INSTALLED
156
+ }
157
+
158
  def clean_latex_code(latex_code):
159
  """Extrait le code LaTeX brut des blocs de code formatés (```latex ... ```)."""
160
  logger.info("Nettoyage du code LaTeX reçu de Gemini...")
 
161
  match_latex = re.search(r"```(?:latex|tex)\s*(.*?)\s*```", latex_code, re.DOTALL | re.IGNORECASE)
162
  if match_latex:
163
  logger.info("Bloc de code 'latex' ou 'tex' trouvé et extrait.")
164
  return match_latex.group(1).strip()
165
 
 
166
  match_generic = re.search(r"```\s*(\\documentclass.*?)\s*```", latex_code, re.DOTALL | re.IGNORECASE)
167
  if match_generic:
168
  logger.info("Bloc de code générique avec '\\documentclass' trouvé et extrait.")
 
184
  logger.info(f"Début de la compilation LaTeX vers PDF pour '{output_filename_base}'")
185
 
186
  try:
 
187
  with open(tex_path, "w", encoding="utf-8") as tex_file:
188
  tex_file.write(latex_code)
189
  logger.info(f"Fichier .tex '{tex_path}' créé avec succès.")
190
 
 
191
  my_env = os.environ.copy()
192
  my_env["LC_ALL"] = "C.UTF-8"
193
  my_env["LANG"] = "C.UTF-8"
194
 
195
  last_result = None
 
196
  for i in range(2):
197
  logger.info(f"Exécution de pdflatex - Passe {i+1}/2...")
198
  process = subprocess.run(
 
200
  capture_output=True, text=True, check=False, encoding="utf-8", errors="replace", env=my_env, timeout=60
201
  )
202
  last_result = process
 
203
  if not os.path.exists(pdf_path) and process.returncode != 0:
204
  logger.warning(f"La passe {i+1} de pdflatex a échoué et aucun PDF n'a été créé. Arrêt de la compilation.")
205
  break
 
210
  else:
211
  error_log = last_result.stdout + "\n" + last_result.stderr if last_result else "Aucun résultat de compilation disponible."
212
  logger.error(f"Échec de la compilation PDF pour '{tex_filename}'. Log de pdflatex:\n{error_log}")
213
+ return None, f"Erreur de compilation PDF. Log: ...{error_log[-1000:]}"
214
 
215
  except Exception as e:
216
  logger.error(f"Exception pendant la génération du PDF: {e}", exc_info=True)
 
232
  logger.info(log_msg)
233
  data = {'chat_id': TELEGRAM_CHAT_ID, 'caption': caption}
234
  response = requests.post(url, files=files, data=data, timeout=30)
235
+ response.raise_for_status()
236
  logger.info(f"Fichier '{filename}' envoyé avec succès à Telegram.")
237
  except Exception as e:
238
  logger.error(f"Erreur lors de l'envoi à Telegram: {e}", exc_info=True)
239
 
 
240
  # --- Logique Principale (Worker en arrière-plan) ---
241
 
242
  def process_files_background(task_id, files_data, resolution_style):
 
254
  for file_info in files_data:
255
  if file_info['type'].startswith('image/'):
256
  logger.info(f"[Task {task_id}] Traitement de l'image '{file_info['filename']}'.")
257
+
258
+ # Sauvegarder l'image utilisateur
259
+ saved_filename = save_user_image(file_info['data'], file_info['filename'], task_id)
260
+
261
  img = Image.open(io.BytesIO(file_info['data']))
262
  buffered = io.BytesIO()
263
+ img.save(buffered, format="PNG")
264
  img_base64_str = base64.b64encode(buffered.getvalue()).decode()
265
  contents.append({'inline_data': {'mime_type': 'image/png', 'data': img_base64_str}})
266
 
 
270
  temp_pdf.write(file_info['data'])
271
  temp_pdf_path = temp_pdf.name
272
 
 
273
  file_ref = client.files.upload(file=temp_pdf_path)
274
  uploaded_file_refs.append(file_ref)
275
  contents.append(file_ref)
276
+ os.unlink(temp_pdf_path)
277
  logger.info(f"[Task {task_id}] PDF '{file_info['filename']}' uploadé avec succès. Référence: {file_ref.name}")
278
 
279
  if not contents:
 
307
  cleaned_latex = clean_latex_code(full_latex_response)
308
  logger.debug(f"[Task {task_id}] Code LaTeX nettoyé:\n---\n{cleaned_latex[:500]}...\n---")
309
 
 
310
  task_results[task_id]['status'] = 'generating_pdf'
311
  pdf_filename_base = f"solution_{task_id}"
312
  pdf_file_path, pdf_message = latex_to_pdf(cleaned_latex, pdf_filename_base, GENERATED_PDF_DIR)
 
325
  task_results[task_id]['error'] = str(e)
326
  task_results[task_id]['response'] = f"Une erreur est survenue: {str(e)}"
327
  finally:
 
328
  if uploaded_file_refs:
329
  logger.info(f"[Task {task_id}] Nettoyage des {len(uploaded_file_refs)} fichiers temporaires de l'API Gemini.")
330
  for file_ref in uploaded_file_refs:
 
341
  logger.info(f"Requête servie pour l'endpoint '/' depuis {request.remote_addr}")
342
  return render_template('index.html')
343
 
344
+ @app.route('/admin')
345
+ def admin_panel():
346
+ """Centre de gestion complet."""
347
+ logger.info(f"Accès au centre de gestion depuis {request.remote_addr}")
348
+ tasks_info = get_all_tasks_info()
349
+ system_stats = get_system_stats()
350
+
351
+ return render_template('admin.html', tasks=tasks_info, stats=system_stats)
352
+
353
+ @app.route('/admin/api/tasks')
354
+ def admin_api_tasks():
355
+ """API JSON pour récupérer les informations des tâches."""
356
+ tasks_info = get_all_tasks_info()
357
+ return jsonify(tasks_info)
358
+
359
+ @app.route('/admin/api/stats')
360
+ def admin_api_stats():
361
+ """API JSON pour récupérer les statistiques système."""
362
+ stats = get_system_stats()
363
+ return jsonify(stats)
364
+
365
+ @app.route('/admin/delete_task/<task_id>', methods=['POST'])
366
+ def admin_delete_task(task_id):
367
+ """Supprime une tâche et ses fichiers associés."""
368
+ logger.info(f"Demande de suppression de la tâche {task_id}")
369
+
370
+ if task_id not in task_results:
371
+ return jsonify({'error': 'Tâche introuvable'}), 404
372
+
373
+ try:
374
+ task_data = task_results[task_id]
375
+
376
+ # Supprimer le PDF s'il existe
377
+ if 'pdf_filename' in task_data:
378
+ pdf_path = os.path.join(GENERATED_PDF_DIR, task_data['pdf_filename'])
379
+ if os.path.exists(pdf_path):
380
+ os.remove(pdf_path)
381
+ logger.info(f"PDF supprimé: {pdf_path}")
382
+
383
+ # Supprimer les images utilisateur associées
384
+ if os.path.exists(USER_IMAGES_DIR):
385
+ for img_file in os.listdir(USER_IMAGES_DIR):
386
+ if img_file.startswith(f"{task_id}_"):
387
+ img_path = os.path.join(USER_IMAGES_DIR, img_file)
388
+ os.remove(img_path)
389
+ logger.info(f"Image utilisateur supprimée: {img_path}")
390
+
391
+ # Supprimer la tâche de la mémoire
392
+ del task_results[task_id]
393
+
394
+ logger.info(f"Tâche {task_id} supprimée avec succès")
395
+ return jsonify({'success': True})
396
+
397
+ except Exception as e:
398
+ logger.error(f"Erreur lors de la suppression de la tâche {task_id}: {e}")
399
+ return jsonify({'error': str(e)}), 500
400
+
401
+ @app.route('/user_images/<filename>')
402
+ def serve_user_image(filename):
403
+ """Sert les images utilisateur."""
404
+ try:
405
+ return send_from_directory(USER_IMAGES_DIR, filename)
406
+ except FileNotFoundError:
407
+ return "Image non trouvée", 404
408
+
409
  @app.route('/solve', methods=['POST'])
410
  def solve():
411
  logger.info(f"Nouvelle requête sur /solve depuis {request.remote_addr}")
 
428
  file_data = file.read()
429
  file_type = file.content_type or 'application/octet-stream'
430
 
 
431
  if file_type.startswith('image/'):
432
  file_count['images'] += 1
433
  files_data.append({'filename': file.filename, 'data': file_data, 'type': file_type})
 
505
  logger.info(f"Fermeture de la connexion SSE pour la tâche terminée/échouée {task_id}")
506
  break
507
 
508
+ time.sleep(1)
509
 
510
  return Response(stream_with_context(generate()), mimetype='text/event-stream', headers={'Cache-Control': 'no-cache', 'X-Accel-Buffering': 'no'})
511
 
 
527
  if __name__ == '__main__':
528
  logger.info("Démarrage de l'application Flask.")
529
 
530
+ # Création des répertoires nécessaires
531
  os.makedirs(GENERATED_PDF_DIR, exist_ok=True)
532
+ os.makedirs(USER_IMAGES_DIR, exist_ok=True)
533
+ logger.info(f"Répertoires assurés d'exister: '{GENERATED_PDF_DIR}' et '{USER_IMAGES_DIR}'")
534
 
535
+ app.run(debug=True, host='0.0.0.0', port=5000)