## class to generate the DOCX report import json from docx import Document from docx.shared import Pt, RGBColor, Inches from docx.enum.text import WD_ALIGN_PARAGRAPH from docx.enum.section import WD_SECTION from docx.oxml import OxmlElement from docx.oxml.ns import qn from typing import Dict, Any, List, Union # Ajout des imports typing nécessaires import logging class RapportGenerator: def __init__(self, json_file: str, docx_path: str): self.doc = Document() self.logger = logging.getLogger(__name__) logging.basicConfig(level=logging.INFO) self.json_data = self.load_json(json_file) self.docx_path = docx_path self._setup_document() def load_json(self, json_file: str) -> Dict: """Charge le fichier JSON""" try: with open(json_file, 'r', encoding='utf-8') as f: return json.load(f) except Exception as e: self.logger.error(f"Erreur lors de la lecture du JSON: {str(e)}") raise def _setup_document(self): """Configure le document""" sections = self.doc.sections for section in sections: section.bottom_margin = Inches(1) self._create_page_number() def _create_page_number(self): """Crée le numéro de page dans le pied de page""" section = self.doc.sections[0] footer = section.footer paragraph = footer.paragraphs[0] paragraph.alignment = WD_ALIGN_PARAGRAPH.RIGHT run = paragraph.add_run() self._create_element(run, 'PAGE') run = paragraph.add_run(" / ") run = paragraph.add_run() self._create_element(run, 'NUMPAGES') def _create_element(self, run, text): """Crée un élément de champ Word""" fldChar1 = OxmlElement('w:fldChar') fldChar1.set(qn('w:fldCharType'), 'begin') run._r.append(fldChar1) instrText = OxmlElement('w:instrText') instrText.text = text run._r.append(instrText) fldChar2 = OxmlElement('w:fldChar') fldChar2.set(qn('w:fldCharType'), 'end') run._r.append(fldChar2) def _add_title(self, text: str, level: int = 1): """Ajoute un titre avec style et taille appropriés""" heading = self.doc.add_heading(level=level) heading.alignment = WD_ALIGN_PARAGRAPH.LEFT run = heading.add_run(text.strip()) # Ajout de strip() font_sizes = {0: 18, 1: 16, 2: 14, 3: 12} run.font.size = Pt(font_sizes.get(level, 12)) def _format_key(self, key: str) -> str: """Formate la clé en supprimant les suffixes _DEMANDEUR et _DEFENDEUR""" key = key.replace("_", " ").title() key = key.replace(" Demandeur", "") key = key.replace(" Defendeur", "") return key def _get_safe_value(self, data: Dict, key: str, default: str = '') -> str: """Récupère une valeur en toute sécurité du dictionnaire""" if isinstance(data, dict): return str(data.get(key, default)) return default def _format_person_info(self, data: Dict, is_lawyer: bool = False) -> Dict[str, str]: """Formate les informations d'une personne""" info = {} if not isinstance(data, dict): return info if is_lawyer: # Informations de l'avocat civilite = self._get_safe_value(data, 'CIVILITE_AVOCAT_DEMANDEUR') or self._get_safe_value(data, 'CIVILITE_AVOCAT_DEFENDEUR') prenom = self._get_safe_value(data, 'PRENOM_AVOCAT_DEMANDEUR') or self._get_safe_value(data, 'PRENOM_AVOCAT_DEFENDEUR') nom = self._get_safe_value(data, 'NOM_AVOCAT_DEMANDEUR') or self._get_safe_value(data, 'NOM_AVOCAT_DEFENDEUR') info['Nom'] = f"{civilite} {prenom} {nom}".strip() info['Barreau'] = self._get_safe_value(data, 'BARREAU_AVOCAT_DEMANDEUR') or self._get_safe_value(data, 'BARREAU_AVOCAT_DEFENDEUR') info['Bureau d\'avocats'] = self._get_safe_value(data, 'BUREAU_AVOCAT_DEMANDEUR') or self._get_safe_value(data, 'BUREAU_AVOCAT_DEFENDEUR') adresse = data.get('ADRESSE_AVOCAT_DEMANDEUR', {}) if isinstance(data.get('ADRESSE_AVOCAT_DEMANDEUR'), dict) else {} if not adresse: adresse = data.get('ADRESSE_AVOCAT_DEFENDEUR', {}) if isinstance(data.get('ADRESSE_AVOCAT_DEFENDEUR'), dict) else {} info['Adresse Complete'] = self._get_safe_value(adresse, 'ADRESSE_COMPLETE_AVOCAT_DEMANDEUR') or self._get_safe_value(adresse, 'ADRESSE_COMPLETE_AVOCAT_DEFENDEUR') info['Email'] = self._get_safe_value(data, 'EMAIL_AVOCAT_DEMANDEUR') or self._get_safe_value(data, 'EMAIL_AVOCAT_DEFENDEUR') info['Telephone'] = self._get_safe_value(data, 'TELEPHONE_AVOCAT_DEMANDEUR') or self._get_safe_value(data, 'TELEPHONE_AVOCAT_DEFENDEUR') else: # Informations de la partie civilite = self._get_safe_value(data, 'CIVILITE_DEMANDEUR') or self._get_safe_value(data, 'CIVILITE_DEFENDEUR') prenom = self._get_safe_value(data, 'PRENOM_DEMANDEUR') or self._get_safe_value(data, 'PRENOM_DEFENDEUR') nom = self._get_safe_value(data, 'NOM_DEMANDEUR') or self._get_safe_value(data, 'NOM_DEFENDEUR') info['Nom'] = f"{civilite} {prenom} {nom}".strip() info['Organisme'] = self._get_safe_value(data, 'ORGANISME_DEMANDEUR') or self._get_safe_value(data, 'ORGANISME_DEFENDEUR') adresse = data.get('ADRESSE_DEMANDEUR', {}) if isinstance(data.get('ADRESSE_DEMANDEUR'), dict) else {} if not adresse: adresse = data.get('ADRESSE_DEFENDEUR', {}) if isinstance(data.get('ADRESSE_DEFENDEUR'), dict) else {} info['Adresse Complete'] = self._get_safe_value(adresse, 'ADRESSE_COMPLETE_DEMANDEUR') or self._get_safe_value(adresse, 'ADRESSE_COMPLETE_DEFENDEUR') info['Email'] = self._get_safe_value(data, 'EMAIL_DEMANDEUR') or self._get_safe_value(data, 'EMAIL_DEFENDEUR') info['Telephone'] = self._get_safe_value(data, 'TELEPHONE_DEMANDEUR') or self._get_safe_value(data, 'TELEPHONE_DEFENDEUR') return {k: v for k, v in info.items() if v and v != "Non spécifié"} def _process_special_fields(self, key: str, value: Any) -> bool: """Traite les champs spéciaux (HISTORIQUE et MISSION)""" if key in ["HISTORIQUE", "MISSION"]: self._add_title(key.upper().strip(), 1) # Vérifier si la valeur est une liste if isinstance(value, list): for item in value: p = self.doc.add_paragraph() p.add_run(str(item).strip()) else: # Si c'est une chaîne simple ou autre type p = self.doc.add_paragraph() p.add_run(str(value).strip()) # Ajouter un espace après HISTORIQUE seulement if key == "HISTORIQUE": self.doc.add_paragraph() return True return False def _process_parties(self, title: str, items: Union[str, List], level: int): """Traite les parties (demandeurs/défendeurs)""" self._add_title(title, level) # Si aucune partie n'est spécifiée if isinstance(items, str) and items == "Non spécifié": p = self.doc.add_paragraph(style='List Bullet') p.add_run("Non spécifié") return # Traitement de chaque partie if isinstance(items, list): for idx, item in enumerate(items, 1): if not isinstance(item, dict): continue # Sous-titre pour chaque partie partie_title = f"{title.rstrip('S')} {idx}" self._add_title(partie_title, level + 1) # Formatage du nom complet civilite = self._get_safe_value(item, f'CIVILITE_{title.rstrip("S")}', '') prenom = self._get_safe_value(item, f'PRENOM_{title.rstrip("S")}', '') nom = self._get_safe_value(item, f'NOM_{title.rstrip("S")}', '') nom_complet = f"{civilite} {prenom} {nom}".strip() # Ajout du nom if nom_complet and nom_complet != "Non spécifié": p = self.doc.add_paragraph(style='List Bullet') p.add_run("Nom: ").bold = True p.add_run(nom_complet) # Récupération de l'adresse adresse = item.get(f'ADRESSE_{title.rstrip("S")}', {}) if isinstance(adresse, dict): adresse_complete = adresse.get(f'ADRESSE_COMPLETE_{title.rstrip("S")}', '') if adresse_complete and adresse_complete != "Non spécifié": p = self.doc.add_paragraph(style='List Bullet') p.add_run("Adresse Complete: ").bold = True p.add_run(adresse_complete) # Ajout du téléphone telephone = self._get_safe_value(item, f'TELEPHONE_{title.rstrip("S")}', '') if telephone and telephone != "Non spécifié": p = self.doc.add_paragraph(style='List Bullet') p.add_run("Téléphone: ").bold = True p.add_run(telephone) # Ajout de l'email email = self._get_safe_value(item, f'EMAIL_{title.rstrip("S")}', '') if email and email != "Non spécifié": p = self.doc.add_paragraph(style='List Bullet') p.add_run("Email: ").bold = True p.add_run(email) # Traitement des avocats avocat_key = f'AVOCAT_{title.rstrip("S")}' avocats = item.get(avocat_key, []) if isinstance(avocats, list) and avocats and avocats != "Non spécifié": for avocat_idx, avocat in enumerate(avocats, 1): if isinstance(avocat, dict): # Sous-titre pour chaque avocat self._add_title(f"Avocat {avocat_idx}", level + 2) # Nom de l'avocat civilite_avocat = self._get_safe_value(avocat, f'CIVILITE_AVOCAT_{title.rstrip("S")}', '') prenom_avocat = self._get_safe_value(avocat, f'PRENOM_AVOCAT_{title.rstrip("S")}', '') nom_avocat = self._get_safe_value(avocat, f'NOM_AVOCAT_{title.rstrip("S")}', '') nom_complet_avocat = f"{civilite_avocat} {prenom_avocat} {nom_avocat}".strip() if nom_complet_avocat and nom_complet_avocat != "Non spécifié": p = self.doc.add_paragraph(style='List Bullet') p.add_run("Nom: ").bold = True p.add_run(nom_complet_avocat) # Barreau barreau = self._get_safe_value(avocat, f'BARREAU_AVOCAT_{title.rstrip("S")}', '') if barreau and barreau != "Non spécifié": p = self.doc.add_paragraph(style='List Bullet') p.add_run("Barreau: ").bold = True p.add_run(barreau) # Bureau d'avocats bureau = self._get_safe_value(avocat, f'BUREAU_AVOCAT_{title.rstrip("S")}', '') if bureau and bureau != "Non spécifié": p = self.doc.add_paragraph(style='List Bullet') p.add_run("Bureau d'avocats: ").bold = True p.add_run(bureau) # Adresse de l'avocat adresse_avocat = avocat.get(f'ADRESSE_AVOCAT_{title.rstrip("S")}', {}) if isinstance(adresse_avocat, dict): adresse_complete_avocat = adresse_avocat.get(f'ADRESSE_COMPLETE_AVOCAT_{title.rstrip("S")}', '') if adresse_complete_avocat and adresse_complete_avocat != "Non spécifié": p = self.doc.add_paragraph(style='List Bullet') p.add_run("Adresse Complete: ").bold = True p.add_run(adresse_complete_avocat) # Téléphone de l'avocat tel_avocat = self._get_safe_value(avocat, f'TELEPHONE_AVOCAT_{title.rstrip("S")}', '') if tel_avocat and tel_avocat != "Non spécifié": p = self.doc.add_paragraph(style='List Bullet') p.add_run("Téléphone: ").bold = True p.add_run(tel_avocat) # Email de l'avocat email_avocat = self._get_safe_value(avocat, f'EMAIL_AVOCAT_{title.rstrip("S")}', '') if email_avocat and email_avocat != "Non spécifié": p = self.doc.add_paragraph(style='List Bullet') p.add_run("Email: ").bold = True p.add_run(email_avocat) # Ajouter un espace entre chaque partie if idx < len(items): self.doc.add_paragraph() def _add_table_of_contents(self): """Ajoute une table des matières au document""" paragraph = self.doc.add_paragraph() run = paragraph.add_run("TABLE DES MATIÈRES") run.bold = True run.font.size = Pt(14) self.doc.add_paragraph() paragraph = self.doc.add_paragraph() # Ajouter le champ TOC run = paragraph.add_run() fldChar1 = OxmlElement('w:fldChar') fldChar1.set(qn('w:fldCharType'), 'begin') run._r.append(fldChar1) instrText = OxmlElement('w:instrText') instrText.set(qn('xml:space'), 'preserve') instrText.text = 'TOC \\o "1-3" \\h \\z \\u' run._r.append(instrText) fldChar2 = OxmlElement('w:fldChar') fldChar2.set(qn('w:fldCharType'), 'separate') run._r.append(fldChar2) # Ajouter un espace pour la mise à jour run = paragraph.add_run() fldChar4 = OxmlElement('w:fldChar') fldChar4.set(qn('w:fldCharType'), 'end') run._r.append(fldChar4) self.doc.add_page_break() def _format_person_info_simple(self, data: Dict, prefix: str) -> str: """Formate les informations d'une personne (magistrat ou greffier)""" prenom = data.get(f'PRENOM_{prefix}', '').strip() nom = data.get(f'NOM_{prefix}', '').strip() titre = data.get(f'TITRE_{prefix}', '').strip() return f"{prenom} {nom}, {titre}".strip() def _format_monetary_value(self, data: Dict, prefix: str) -> str: """Formate les valeurs monétaires""" try: valeur = str(data.get(f'VALEUR_{prefix}', 0)).strip() if valeur == "None": valeur = "0.0" except: valeur = "0.0" try: monnaie = data.get(f'MONNAIE_{prefix}', '€').strip() except: monnaie = "euros" if valeur == "0.0": return "Non spécifié" else: return f"{valeur} {monnaie}".strip() def generate_report(self): """Génère le rapport complet""" try: # Titre principal title = self.doc.add_heading("Données extraites d'une ordonnance de référé", 0) title.alignment = WD_ALIGN_PARAGRAPH.CENTER # Informations générales self._add_title("INFORMATIONS GÉNÉRALES", 1) if isinstance(self.json_data.get("INFORMATIONS_GENERALES"), dict): info_gen = self.json_data["INFORMATIONS_GENERALES"] # Liste des champs à traiter fields_to_process = [ # Champs simples ("TRIBUNAL_ORDONNANCE_REFERE", "Tribunal"), ("DATE_ORDONNANCE_REFERE", "Date Ordonnance Référé"), ("REFERENCE_DOSSIER", "Référence Dossier"), ("REFERENCE_PORTALIS", "Référence Portalis"), # Champs complexes avec formatage spécial ("MAGISTRAT_ORDONNANCE_REFERE", "Magistrat", lambda x: self._format_person_info_simple(x, "MAGISTRAT_ORDONNANCE_REFERE")), ("GREFFIER_ORDONNANCE_REFERE", "Greffier", lambda x: self._format_person_info_simple(x, "GREFFIER_ORDONNANCE_REFERE")), ("CONSIGNATION", "Consignation", lambda x: self._format_monetary_value(x, "CONSIGNATION")), #("SOMME_A_CONSIGER", "Somme A Consiger", #lambda x: self._format_monetary_value(x, "SOMME_A_CONSIGER")) ] # Traitement de chaque champ for field in fields_to_process: key = field[0] display_name = field[1] if key in info_gen: p = self.doc.add_paragraph(style='List Bullet') p.add_run(f"{display_name}: ").bold = True # Si c'est un champ avec formatage spécial (longueur du tuple = 3) if len(field) == 3: formatter = field[2] value = formatter(info_gen[key]) else: value = str(info_gen[key]) p.add_run(value) # Traitement des demandeurs if "DEMANDEURS" in self.json_data: self._process_parties("DEMANDEURS", self.json_data["DEMANDEURS"], 1) # Traitement des défendeurs if "DEFENDEURS" in self.json_data: self._process_parties("DEFENDEURS", self.json_data["DEFENDEURS"], 1) # Historique if "HISTORIQUE" in self.json_data: self._process_special_fields("HISTORIQUE", self.json_data["HISTORIQUE"]) # Mission if "MISSION" in self.json_data: self._process_special_fields("MISSION", self.json_data["MISSION"]) # Sauvegarde du document self.doc.save(self.docx_path) self.logger.info(f"Rapport généré avec succès: {self.docx_path}") return self.docx_path except Exception as e: self.logger.error(f"Erreur lors de la génération du rapport: {str(e)}") raise