import PyPDF2 import requests import json from reportlab.lib.pagesizes import letter from reportlab.lib.units import inch from reportlab.platypus import SimpleDocTemplate, Paragraph, Spacer from reportlab.lib.styles import getSampleStyleSheet, ParagraphStyle from reportlab.lib.enums import TA_JUSTIFY from reportlab.pdfbase import pdfmetrics from reportlab.pdfbase.ttfonts import TTFont import os, time # Configuration DEBUG = True PDF_PATH = "Traduction/TaniaBorecMemoir(Ukr).pdf" OLLAMA_MODEL = "traductionUkrainienVersFrancais:latest" OLLAMA_URL = "http://localhost:11434/api/generate" TARGET_LANGUAGE = "français" CHECKPOINT_FILE = "Traduction/checkpoint.json" TEMP_OUTPUT_TXT = "Traduction/output_temp.txt" FINAL_OUTPUT_PDF = PDF_PATH.replace(".pdf",f"({TARGET_LANGUAGE.upper()[:2]})_V9.pdf") FINAL_OUTPUT_TXT = PDF_PATH.replace(".pdf",f"({TARGET_LANGUAGE.upper()[:2]})_V9.txt") DEBUG = True def extract_parameters_from_template(template_str): """Extrait les paramètres du modèle à partir du template.""" import re parameters = {} if not template_str or not isinstance(template_str, str): return parameters # Si la chaîne contient "parameters:", récupère ce qui suit if 'parameters:' in template_str: params_section = template_str.split('parameters:', 1)[1] else: # Sinon, utilise la chaîne directement (elle contient déjà les paramètres) params_section = template_str # Parse les lignes de paramètres # Format: "stop """ # "temperature 0.1" lines = params_section.split('\n') for line in lines: if not line.strip(): continue # Divise par le premier groupe d'espaces blancs # Cela sépare la clé des valeurs avec leurs espaces parts = line.split(None, 1) # split() avec maxsplit=1 divise sur les espaces if len(parts) == 2: param_name = parts[0].strip() param_value = parts[1].strip() parameters[param_name] = param_value return parameters def get_llm_model_info(model=OLLAMA_MODEL): """ Extrait les informations du modèle LLM depuis Ollama, y compris le nom depuis la ligne FROM du Modelfile. @param model: Nom du modèle à interroger. @type model: str @return: Dictionnaire contenant les informations du modèle, ou None en cas d'erreur. @rtype: dict | None """ try: # Chemin vers le fichier Modelfile (supposé être dans le même répertoire que le script) modelfile_path = os.path.join(os.path.dirname(__file__), "Modelfile") # Initialisation de model_name model_name = "none" # Lecture du fichier Modelfile pour extraire le nom du modèle if os.path.exists(modelfile_path): with open(modelfile_path, 'r', encoding='utf-8') as file: for line in file: if line.strip().startswith('FROM '): model_name = line.strip().split('FROM ')[1].strip() break # URL pour obtenir les informations du modèle info_url = OLLAMA_URL.replace("/api/generate", "/api/show") payload = {"name": model} response = requests.post(info_url, json=payload) if response.status_code == 200: model_data = response.json() # Gère le cas où model_data est une chaîne if isinstance(model_data, str): model_data = json.loads(model_data) # Extrait les paramètres du template parameters = model_data.get('parameters', '') parsed_params = extract_parameters_from_template(parameters) # Extraction du nom depuis la ligne FROM modelfile_content = model_data.get('Modelfile', '') # Extraction des informations principales info = { "temperature": parsed_params.get('temperature', model_data.get("temperature", "Not available")), "name": model_name, "num_ctx": parsed_params.get('num_ctx', "Not available"), "top_k": parsed_params.get('top_k', "Not available"), "top_p": parsed_params.get('top_p', "Not available"), "system": model_data.get("system", "Not available"), "modified_at": model_data.get("modified_at", "Not available"), } return info else: print(f"Erreur lors de la récupération du modèle : {response.text}") return None except Exception as e: print(f"Erreur lors de l'accès aux informations du modèle : {e}") return None def display_llm_info(): """Retourne les informations du modèle LLM formatées.""" from datetime import datetime info = get_llm_model_info(OLLAMA_MODEL) if info: # Formate la date en jj/mm/AAAA modified_at = info.get('modified_at', 'Not available') if modified_at and modified_at != 'Not available': try: # Parse la date ISO date_obj = datetime.fromisoformat(modified_at.replace('Z', '+00:00')) # Formate en jj/mm/AAAA formatted_date = date_obj.strftime("%d/%m/%Y") except: formatted_date = modified_at else: formatted_date = 'Not available' return f"LLM Modèle: {info['name']}
\nDate de modification: {formatted_date}
\nSystem: {info['system']}
\nTemperature: {info['temperature']}" else: return "Informations du modèle non disponibles" def register_unicode_font(): """Enregistre une police TrueType qui supporte le cyrillique.""" # Recherche une police système qui supporte le cyrillique font_paths = [ r"C:\Windows\Fonts\DejaVuSans.ttf", r"C:\Windows\Fonts\Calibri.ttf", r"C:\Windows\Fonts\arial.ttf", ] for font_path in font_paths: if os.path.exists(font_path): try: pdfmetrics.registerFont(TTFont('UnicodeFont', font_path)) return 'UnicodeFont' except Exception as e: print(f"Erreur lors de l'enregistrement de {font_path}: {e}") # Si aucune police spéciale trouvée, utilise Helvetica par défaut print("Aucune police Unicode trouvée, utilisation d'Helvetica") return 'Helvetica' # Charge ou initialise le checkpoint def load_checkpoint(): if os.path.exists(CHECKPOINT_FILE): with open(CHECKPOINT_FILE, "r") as f: return json.load(f) return {"last_processed_index": -1, "results": {}} # Sauvegarde le checkpoint # Sauvegarde le checkpoint def save_checkpoint(last_index, results): # Trier les clés du dictionnaire results sorted_results = {key: results[key] for key in sorted(results.keys(), key=int)} with open(CHECKPOINT_FILE, "w") as f: # Utiliser un espace d'indentation de 4 espaces pour rendre le JSON plus lisible json.dump({"last_processed_index": last_index, "results": sorted_results}, f, indent=4) # Sauvegarde les résultats temporaires dans un fichier TXT def save_temp_results(results): with open(TEMP_OUTPUT_TXT, "w", encoding="utf-8") as f: for idx, translation in results.items(): f.write(f"Paragraphe {idx}:\n{translation}\n\n") # Extraction du texte du PDF (inchangée) def extract_text_from_pdf(pdf_path): text_by_page = [] with open(pdf_path, "rb") as file: reader = PyPDF2.PdfReader(file) for page in reader.pages: text = page.extract_text() text_by_page.append(text) return text_by_page # Découpage en paragraphes (inchangé) def split_pages_in_paragraphs(pages_text): import re full_text = "\n".join(pages_text) full_text = re.sub(r'(?") if DEBUG: # Ajoute le paragraphe avec sa note story.append(Paragraph(paragraph_num, note_style)) story.append(Paragraph(formatted_text, body_style)) # Infos sur le LLM story.append(Spacer(1, 0.2*inch)) story.append(Paragraph(display_llm_info(), page_style)) # Construction du PDF doc.build(story) print(f"PDF généré avec succès : {output_path}") def create_txt_from_results(results, output_path): """Crée un fichier TXT à partir des résultats de traduction.""" OUTPUT_TXT_PATH = output_path.replace(".pdf", f".txt") # Chemin du fichier TXT de sortie # Titre avec la langue cible title_text = f"Traduction - Ukrainien vers {TARGET_LANGUAGE.capitalize()}" with open(OUTPUT_TXT_PATH, 'w', encoding='utf-8') as txt_file: txt_file.write(title_text + "\n\n") # Contenu for paragraph_num, translation in results.items(): # Ajoute les numéro de paragraphe et chapitre if(DEBUG): txt_file.write(f"{paragraph_num}\n") # Préserver la mise en page en convertissant les sauts de ligne txt_file.write(translation + "\n\n") # Infos sur le LLM txt_file.write("\n") txt_file.write(display_llm_info() + "\n") # Fonction principale def main(): checkpoint = load_checkpoint() last_index = checkpoint["last_processed_index"] results = checkpoint["results"] pages = extract_text_from_pdf(PDF_PATH) paragraphs = split_pages_in_paragraphs(pages) # Liste de tous les indices de batches attendus (par pas de batch_size) batch_size = 5 expected_batch_indices = list(range(0, len(paragraphs), batch_size)) # Liste des indices de batches déjà présents dans results present_batch_indices = set() for key in results.keys(): batch_start = int(int(key) // batch_size * batch_size) # Arrondit à l'indice de début de batch present_batch_indices.add(batch_start) # Trouve les batches manquants missing_batches = [i for i in expected_batch_indices if i not in present_batch_indices and i <= last_index] # Affichage des batches manquants (pour débogage) print(f"Batches manquants détectés : {missing_batches}") # Traduction des paragraphes manquants temps_cumule = 0.0 for i in missing_batches: batch = paragraphs[i:i + batch_size] paragraph_cumul = "\n".join(batch) print(f"{15 * '-'} Traduction des paragraphes manquants {i+1} à {min(i + batch_size, len(paragraphs))} / {len(paragraphs)}") try: debut_chrono = time.time() result = send_to_ollama(paragraph_cumul) fin_chrono = time.time() temps_paragraphe = fin_chrono - debut_chrono temps_cumule += temps_paragraphe # Conversion en minutes et secondes minutes_paragraphe, secondes_paragraphe = divmod(temps_paragraphe, 60) minutes_cumule, secondes_cumule = divmod(temps_cumule, 60) print(f"{result}") results[str(i)] = result save_checkpoint(len(paragraphs), results) # Met à jour le dernier indice du batch save_temp_results(results) except Exception as e: print(f"Erreur lors de la traduction du paragraphe {i}: {e}") print(f" Temps de traduction : {int(minutes_paragraphe)} min {secondes_paragraphe:.2f} sec") print(f" Temps cumulé : {int(minutes_cumule)} min {secondes_cumule:.2f} sec") # Traitement des paragraphes suivants for i in range(last_index + 1, len(paragraphs), batch_size): batch = paragraphs[i:i + batch_size] paragraph_cumul = "\n".join(batch) print(f"{15 * '-'} Traduction des paragraphes {i+1} à {min(i + batch_size, len(paragraphs))} / {len(paragraphs)}") try: debut_chrono = time.time() result = send_to_ollama(paragraph_cumul) fin_chrono = time.time() temps_paragraphe = fin_chrono - debut_chrono temps_cumule += temps_paragraphe # Conversion en minutes et secondes minutes_paragraphe, secondes_paragraphe = divmod(temps_paragraphe, 60) minutes_cumule, secondes_cumule = divmod(temps_cumule, 60) print(f"{result}") results[str(i)] = result save_checkpoint(i + batch_size - 1, results) save_temp_results(results) except Exception as e: print(f"Erreur : {e}") continue print(f" Temps de traduction : {int(minutes_paragraphe)} min {secondes_paragraphe:.2f} sec") print(f" Temps cumulé : {int(minutes_cumule)} min {secondes_cumule:.2f} sec") save_temp_results(results) create_pdf_from_results(results, FINAL_OUTPUT_PDF) create_txt_from_results(results, FINAL_OUTPUT_TXT) print("Traduction terminée !") if __name__ == "__main__": main()