diff --git a/main.py b/main.py index 2957d7a..8776af6 100644 --- a/main.py +++ b/main.py @@ -1,6 +1,13 @@ 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 from datetime import datetime @@ -14,6 +21,149 @@ TEMP_OUTPUT_TXT = "output_temp.txt" FINAL_OUTPUT_PDF = PDF_PATH.replace(".pdf",f" ({TARGET_LANGUAGE.upper()[:2]})_V2.pdf") FINAL_OUTPUT_TXT = PDF_PATH.replace(".pdf",f" ({TARGET_LANGUAGE.upper()[:2]})_V2.txt") +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 cyrilique.""" + # Recherche une police système qui supporte le cyrilique + 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): @@ -63,24 +213,81 @@ def send_to_ollama(text, target_lang=TARGET_LANGUAGE, model=OLLAMA_MODEL): # Création du PDF final (inchangée) def create_pdf_from_results(results, output_path): - 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 - - doc = SimpleDocTemplate(output_path, pagesize=letter) + """Crée un PDF à partir des résultats de traduction.""" + doc = SimpleDocTemplate(output_path, pagesize=letter, topMargin=inch, bottomMargin=inch) story = [] + + # Enregistre une police qui supporte le cyrilique + font_name = register_unicode_font() + + # Style personnalisé styles = getSampleStyleSheet() - body_style = styles["BodyText"] - - for idx, translation in results.items(): - story.append(Paragraph(translation, body_style)) - + title_style = ParagraphStyle( + 'CustomTitle', + parent=styles['Heading1'], + fontSize=16, + textColor='#1f4788', + spaceAfter=0.3*inch, + alignment=TA_JUSTIFY, + fontName=font_name + ) + + page_style = ParagraphStyle( + 'PageHeading', + parent=styles['Heading2'], + fontSize=12, + textColor='#1f4788', + spaceAfter=0.2*inch, + spaceBefore=0.2*inch, + fontName=font_name + ) + + body_style = ParagraphStyle( + 'CustomBody', + parent=styles['BodyText'], + fontSize=11, + alignment=TA_JUSTIFY, + spaceAfter=0.2*inch, + fontName=font_name + ) + + # Titre avec la langue cible + story.append(Paragraph(f"Traduction - Ukrainien vers {TARGET_LANGUAGE.capitalize()}", title_style)) + story.append(Paragraph(f"Document : {PDF_PATH}", title_style)) + story.append(Spacer(1, 0.2*inch)) + + # Contenu + for page_num, translation in results.items(): + # Préserver la mise en page en convertissant les sauts de ligne + formatted_text = translation.replace("\n", "
") + story.append(Paragraph(formatted_text, body_style)) + # story.append(Spacer(1, 0.1*inch)) + + # 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 final généré : {output_path}") + 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 page_num, translation in results.items(): + # 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(): @@ -114,7 +321,7 @@ def main(): # Génération des fichiers finaux save_temp_results(results) create_pdf_from_results(results, FINAL_OUTPUT_PDF) - os.rename(TEMP_OUTPUT_TXT, FINAL_OUTPUT_TXT) + create_txt_from_results(results, FINAL_OUTPUT_TXT) print("Traduction terminée !") if __name__ == "__main__":