from flask import Flask, render_template, request, jsonify, send_file import threading import time import os import json from datetime import datetime from google import genai from google.genai import types import typing_extensions as typing import uuid app = Flask(__name__) # Configuratiyon GOOGLE_API_KEY = "AIzaSyAMYpF67aqFnWDJESWOx1dC-w3sEU29VcM" # Remplacez par votre clé API MODEL_ID = "gemini-2.5-flash-preview-05-20" # Ou le modèle que vous utilisez UPLOAD_FOLDER = 'uploads' RESULTS_FOLDER = 'results' safety_settings = [ types.SafetySetting( category="HARM_CATEGORY_HARASSMENT", threshold="BLOCK_NONE", ), types.SafetySetting( category="HARM_CATEGORY_HATE_SPEECH", threshold="BLOCK_NONE", ), types.SafetySetting( category="HARM_CATEGORY_SEXUALLY_EXPLICIT", threshold="BLOCK_NONE", ), types.SafetySetting( category="HARM_CATEGORY_DANGEROUS_CONTENT", threshold="BLOCK_NONE", ), ] # Créer les dossiers s'ils n'existent pas os.makedirs(UPLOAD_FOLDER, exist_ok=True) os.makedirs(RESULTS_FOLDER, exist_ok=True) # Définition du schéma JSON pour les réponses class TranslationPair(typing.TypedDict): fang: str francais: str class SyntheticDataResponse(typing.TypedDict): request_number: int generated_pairs: list[TranslationPair] timestamp: str # Stockage des tâches en cours tasks = {} class TaskManager: def __init__(self): self.tasks = {} def create_task(self, task_id): self.tasks[task_id] = { 'status': 'running', 'progress': 0, 'total': 470, 'results_file': f'results_{task_id}.json', # Changé en .json 'start_time': datetime.now(), 'errors': [], 'last_update': datetime.now(), 'all_data': [] # Stocker toutes les données JSON } def update_progress(self, task_id, progress, data=None): if task_id in self.tasks: self.tasks[task_id]['progress'] = progress self.tasks[task_id]['last_update'] = datetime.now() if data: self.tasks[task_id]['all_data'].append(data) def add_error(self, task_id, error): if task_id in self.tasks: self.tasks[task_id]['errors'].append(error) def complete_task(self, task_id): if task_id in self.tasks: self.tasks[task_id]['status'] = 'completed' self.tasks[task_id]['last_update'] = datetime.now() def get_task(self, task_id): return self.tasks.get(task_id) task_manager = TaskManager() def parse_response_to_pairs(response_text, request_num): """Parse la réponse textuelle pour extraire les paires fang/français""" pairs = [] lines = response_text.strip().split('\n') current_fang = "" current_francais = "" for line in lines: line = line.strip() if line.lower().startswith('fang :') or line.lower().startswith('fang:'): current_fang = line.split(':', 1)[1].strip() if ':' in line else line elif line.lower().startswith('français :') or line.lower().startswith('francais:') or line.lower().startswith('français:'): current_francais = line.split(':', 1)[1].strip() if ':' in line else line # Si on a une paire complète, l'ajouter if current_fang and current_francais: pairs.append({ "fang": current_fang, "francais": current_francais }) current_fang = "" current_francais = "" return { "request_number": request_num, "generated_pairs": pairs, "timestamp": datetime.now().isoformat() } def generate_synthetic_data(file_path, task_id): """Fonction qui exécute les 470 requêtes en arrière-plan avec sortie JSON""" try: # Initialiser le client Google AI client = genai.Client(api_key=GOOGLE_API_KEY) # Uploader le fichier file_ref = client.files.upload(file=file_path) # Prompt modifié pour avoir une sortie plus structurée prompt = """J'aimerais générer des nouvelles données synthétiques à partir de ça. Une en fang, une en français. Génère exactement 400 paires de phrases. (respecte strictement cela). """ # Fichier de résultats JSON results_file = os.path.join(RESULTS_FOLDER, f'results_{task_id}.json') # Structure pour stocker toutes les données all_results = { "metadata": { "task_id": task_id, "start_time": datetime.now().isoformat(), "total_requests": 470, "model_used": MODEL_ID }, "requests": [], "summary": { "total_pairs": 0, "completed_requests": 0, "errors": [] } } for i in range(470): try: # Faire la requête avec schéma JSON response = client.models.generate_content( model=MODEL_ID, contents=[file_ref, prompt], config=types.GenerateContentConfig( safety_settings=safety_settings, response_mime_type='application/json', response_schema=SyntheticDataResponse, ) ) try: # Parser la réponse JSON response_data = json.loads(response.text) # Structurer la réponse request_data = { "request_number": i + 1, "timestamp": datetime.now().isoformat(), "response": response_data, "pairs_count": len(response_data.get("pairs", [])) } all_results["requests"].append(request_data) all_results["summary"]["total_pairs"] += request_data["pairs_count"] all_results["summary"]["completed_requests"] += 1 except json.JSONDecodeError: # Si la réponse n'est pas du JSON valide, essayer de parser manuellement parsed_data = parse_response_to_pairs(response.text, i + 1) all_results["requests"].append(parsed_data) all_results["summary"]["total_pairs"] += len(parsed_data.get("generated_pairs", [])) all_results["summary"]["completed_requests"] += 1 # Sauvegarder après chaque requête with open(results_file, 'w', encoding='utf-8') as f: json.dump(all_results, f, ensure_ascii=False, indent=2) # Mettre à jour le progrès task_manager.update_progress(task_id, i + 1) print(f"Requête {i+1}/470 complétée") # Pause pour éviter de surcharger l'API time.sleep(50) except Exception as e: error_msg = f"Erreur requête {i+1}: {str(e)}" task_manager.add_error(task_id, error_msg) all_results["summary"]["errors"].append({ "request_number": i + 1, "error": error_msg, "timestamp": datetime.now().isoformat() }) # Sauvegarder même en cas d'erreur with open(results_file, 'w', encoding='utf-8') as f: json.dump(all_results, f, ensure_ascii=False, indent=2) print(error_msg) # Finaliser le fichier JSON all_results["metadata"]["end_time"] = datetime.now().isoformat() all_results["metadata"]["duration_minutes"] = (datetime.now() - datetime.fromisoformat(all_results["metadata"]["start_time"])).total_seconds() / 60 with open(results_file, 'w', encoding='utf-8') as f: json.dump(all_results, f, ensure_ascii=False, indent=2) task_manager.complete_task(task_id) print(f"Tâche {task_id} terminée avec succès") except Exception as e: error_msg = f"Erreur générale: {str(e)}" task_manager.add_error(task_id, error_msg) print(error_msg) @app.route('/') def index(): return render_template('index.html') @app.route('/upload', methods=['POST']) def upload_file(): if 'file' not in request.files: return jsonify({'error': 'Aucun fichier sélectionné'}), 400 file = request.files['file'] if file.filename == '': return jsonify({'error': 'Aucun fichier sélectionné'}), 400 if file: # Générer un ID unique pour cette tâche task_id = str(uuid.uuid4()) # Sauvegarder le fichier filename = f"input_{task_id}.txt" file_path = os.path.join(UPLOAD_FOLDER, filename) file.save(file_path) # Créer la tâche task_manager.create_task(task_id) # Démarrer le traitement en arrière-plan thread = threading.Thread( target=generate_synthetic_data, args=(file_path, task_id) ) thread.daemon = True thread.start() return jsonify({ 'task_id': task_id, 'message': 'Traitement démarré en arrière-plan' }) @app.route('/status/') def get_status(task_id): task = task_manager.get_task(task_id) if not task: return jsonify({'error': 'Tâche non trouvée'}), 404 return jsonify({ 'status': task['status'], 'progress': task['progress'], 'total': task['total'], 'percentage': round((task['progress'] / task['total']) * 100, 2), 'errors_count': len(task['errors']), 'start_time': task['start_time'].strftime('%Y-%m-%d %H:%M:%S'), 'last_update': task['last_update'].strftime('%Y-%m-%d %H:%M:%S') }) @app.route('/download/') def download_results(task_id): task = task_manager.get_task(task_id) if not task: return jsonify({'error': 'Tâche non trouvée'}), 404 results_file = os.path.join(RESULTS_FOLDER, f'results_{task_id}.json') if not os.path.exists(results_file): return jsonify({'error': 'Fichier de résultats non trouvé'}), 404 # Vérifier si c'est un téléchargement partiel is_partial = request.args.get('partial', 'false').lower() == 'true' if is_partial and task['status'] == 'running': # Créer un fichier temporaire avec les données actuelles temp_file = os.path.join(RESULTS_FOLDER, f'temp_results_{task_id}.json') try: # Charger les données actuelles with open(results_file, 'r', encoding='utf-8') as f: current_data = json.load(f) # Ajouter des métadonnées pour le téléchargement partiel current_data["partial_download"] = { "downloaded_at": datetime.now().isoformat(), "is_partial": True, "progress": f"{task['progress']}/{task['total']}", "percentage": round((task['progress'] / task['total']) * 100, 2) } # Sauvegarder le fichier temporaire with open(temp_file, 'w', encoding='utf-8') as f: json.dump(current_data, f, ensure_ascii=False, indent=2) return send_file( temp_file, as_attachment=True, download_name=f'donnees_synthetiques_partiel_{task_id}.json' ) except Exception as e: return jsonify({'error': f'Erreur lors de la création du fichier partiel: {str(e)}'}), 500 # Téléchargement normal (complet) download_name = f'donnees_synthetiques_{"complet" if task["status"] == "completed" else "actuel"}_{task_id}.json' return send_file( results_file, as_attachment=True, download_name=download_name ) @app.route('/tasks') def list_tasks(): """Liste toutes les tâches""" task_list = [] for task_id, task_info in task_manager.tasks.items(): task_list.append({ 'id': task_id, 'status': task_info['status'], 'progress': task_info['progress'], 'total': task_info['total'], 'percentage': round((task_info['progress'] / task_info['total']) * 100, 2), 'start_time': task_info['start_time'].strftime('%Y-%m-%d %H:%M:%S'), 'last_update': task_info['last_update'].strftime('%Y-%m-%d %H:%M:%S'), 'errors_count': len(task_info['errors']) }) # Trier par heure de début (plus récent en premier) task_list.sort(key=lambda x: x['start_time'], reverse=True) return jsonify(task_list) @app.route('/cleanup') def cleanup_temp_files(): """Nettoyer les fichiers temporaires (optionnel)""" try: temp_files_deleted = 0 for filename in os.listdir(RESULTS_FOLDER): if filename.startswith('temp_results_') and filename.endswith('.json'): file_path = os.path.join(RESULTS_FOLDER, filename) os.remove(file_path) temp_files_deleted += 1 return jsonify({ 'message': f'{temp_files_deleted} fichiers temporaires supprimés' }) except Exception as e: return jsonify({'error': f'Erreur lors du nettoyage: {str(e)}'}), 500 @app.route('/preview/') def preview_results(task_id): """Aperçu des résultats JSON pour debug""" task = task_manager.get_task(task_id) if not task: return jsonify({'error': 'Tâche non trouvée'}), 404 results_file = os.path.join(RESULTS_FOLDER, f'results_{task_id}.json') if not os.path.exists(results_file): return jsonify({'error': 'Fichier de résultats non trouvé'}), 404 try: with open(results_file, 'r', encoding='utf-8') as f: data = json.load(f) # Retourner un aperçu des données preview = { "metadata": data.get("metadata", {}), "summary": data.get("summary", {}), "sample_requests": data.get("requests", [])[:3], # 3 premiers échantillons "total_requests": len(data.get("requests", [])) } return jsonify(preview) except Exception as e: return jsonify({'error': f'Erreur lors de la lecture du fichier: {str(e)}'}), 500 if __name__ == '__main__': print("🚀 Démarrage du serveur...") print("📂 Dossiers créés:", UPLOAD_FOLDER, RESULTS_FOLDER) print("🌐 Application disponible sur: http://localhost:5000") print("📊 Sortie JSON activée") app.run(debug=True, threaded=True)