Spaces:
Running
Running
## 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 | |
("DATE_ORDONNANCE_REFERE", "Date Ordonnance Référé"), | |
("TRIBUNAL_ORDONNANCE_REFERE", "Tribunal"), | |
("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 |