arquiteturia / helpers /rapport_generator.py
pierreguillou's picture
Update helpers/rapport_generator.py
ba9cdf6 verified
raw
history blame
18.9 kB
## 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