|
|
@@ -0,0 +1,704 @@
|
|
|
+{
|
|
|
+ "cells": [
|
|
|
+ {
|
|
|
+ "cell_type": "code",
|
|
|
+ "execution_count": 25,
|
|
|
+ "id": "381dd662",
|
|
|
+ "metadata": {},
|
|
|
+ "outputs": [],
|
|
|
+ "source": [
|
|
|
+ "import pandas as pd \n",
|
|
|
+ "import os \n",
|
|
|
+ "import logging \n",
|
|
|
+ "from pathlib import Path\n",
|
|
|
+ "from datetime import datetime\n",
|
|
|
+ "import re"
|
|
|
+ ]
|
|
|
+ },
|
|
|
+ {
|
|
|
+ "cell_type": "code",
|
|
|
+ "execution_count": 26,
|
|
|
+ "id": "13b3f9ab",
|
|
|
+ "metadata": {},
|
|
|
+ "outputs": [
|
|
|
+ {
|
|
|
+ "name": "stdout",
|
|
|
+ "output_type": "stream",
|
|
|
+ "text": [
|
|
|
+ "c:\\Users\\aiab\\Downloads\\Travaux\\SFCR_extraction_tool\\02 - Inputs\\_QRTs_paramétrages_Abd.xlsx\n"
|
|
|
+ ]
|
|
|
+ }
|
|
|
+ ],
|
|
|
+ "source": [
|
|
|
+ "SCRIPT_DIR = Path.cwd() # 03 - Scripts/\n",
|
|
|
+ "PROJECT_ROOT = SCRIPT_DIR.parent # racine projet\n",
|
|
|
+ "OUTPUTS_DIR = PROJECT_ROOT / \"04 - Outputs\"\n",
|
|
|
+ "BASE_FINALE = OUTPUTS_DIR / \"base_consolidee_QRT.xlsx\"\n",
|
|
|
+ "LOG_FILE = OUTPUTS_DIR / \"consolidation.log\"\n",
|
|
|
+ "\n",
|
|
|
+ "PARAM_DIR = PROJECT_ROOT / \"02 - Inputs\" / \"_QRTs_paramétrages_Abd.xlsx\"\n",
|
|
|
+ "\n",
|
|
|
+ "print(PARAM_DIR)\n"
|
|
|
+ ]
|
|
|
+ },
|
|
|
+ {
|
|
|
+ "cell_type": "code",
|
|
|
+ "execution_count": 27,
|
|
|
+ "id": "611d745d",
|
|
|
+ "metadata": {},
|
|
|
+ "outputs": [],
|
|
|
+ "source": [
|
|
|
+ "# Pattern attendu pour les noms de fichiers QRT\n",
|
|
|
+ "PATTERN_FICHIER = re.compile(\n",
|
|
|
+ " r\"Rapport_S\\.(\\d{2})_page_\\d+\\.xlsx\",\n",
|
|
|
+ " re.IGNORECASE\n",
|
|
|
+ ")\n",
|
|
|
+ " \n",
|
|
|
+ "# Mise en place du logger (console + fichier)\n",
|
|
|
+ "logging.basicConfig(\n",
|
|
|
+ " level=logging.INFO,\n",
|
|
|
+ " format=\"%(asctime)s | %(levelname)-8s | %(message)s\",\n",
|
|
|
+ " datefmt=\"%H:%M:%S\",\n",
|
|
|
+ " handlers=[\n",
|
|
|
+ " logging.StreamHandler(),\n",
|
|
|
+ " logging.FileHandler(LOG_FILE, mode=\"w\", encoding=\"utf-8\"),\n",
|
|
|
+ " ],\n",
|
|
|
+ ")\n",
|
|
|
+ "log = logging.getLogger(__name__)"
|
|
|
+ ]
|
|
|
+ },
|
|
|
+ {
|
|
|
+ "cell_type": "code",
|
|
|
+ "execution_count": 28,
|
|
|
+ "id": "40e02977",
|
|
|
+ "metadata": {},
|
|
|
+ "outputs": [],
|
|
|
+ "source": [
|
|
|
+ "# =============================================================================\n",
|
|
|
+ "# ÉTAPE 1 — Scan récursif : trouver tous les fichiers .xlsx\n",
|
|
|
+ "# =============================================================================\n",
|
|
|
+ "\n",
|
|
|
+ "# Lecture unique du fichier de paramétrage (hors boucle pour la performance)\n",
|
|
|
+ "df_param = pd.read_excel(PARAM_DIR, sheet_name=\"Liste SFCR\", header=3)\n",
|
|
|
+ "df_param.columns = df_param.columns.str.strip()\n",
|
|
|
+ "\n",
|
|
|
+ "# Nettoyage de l'année : supprimer les espaces, convertir en str\n",
|
|
|
+ "df_param[\"Année\"] = (\n",
|
|
|
+ " df_param[\"Année\"]\n",
|
|
|
+ " .astype(str)\n",
|
|
|
+ " .str.replace(\" \", \"\", regex=False)\n",
|
|
|
+ " .str.strip()\n",
|
|
|
+ ")\n",
|
|
|
+ "\n",
|
|
|
+ "# Nettoyage de l'entité : minuscules + strip (pour comparaison insensible à la casse)\n",
|
|
|
+ "df_param[\"entite_clean\"] = (\n",
|
|
|
+ " df_param[\"Entité\"]\n",
|
|
|
+ " .astype(str)\n",
|
|
|
+ " .str.strip()\n",
|
|
|
+ " .str.lower()\n",
|
|
|
+ ")\n",
|
|
|
+ "\n",
|
|
|
+ "# Nettoyage de l'unité : strip des espaces\n",
|
|
|
+ "df_param[\"Unité\"] = df_param[\"Unité\"].astype(str).str.strip()\n",
|
|
|
+ "\n",
|
|
|
+ "\n",
|
|
|
+ "def scanner_fichiers(outputs_dir: Path) -> list[dict]:\n",
|
|
|
+ " \"\"\"\n",
|
|
|
+ " Parcourt 04 - Outputs/{annee}/{entite}/*.xlsx\n",
|
|
|
+ " Retourne une liste de dicts avec les métadonnées de chaque fichier.\n",
|
|
|
+ " \"\"\"\n",
|
|
|
+ " fichiers_trouves = []\n",
|
|
|
+ "\n",
|
|
|
+ " if not outputs_dir.exists():\n",
|
|
|
+ " log.error(f\"Dossier introuvable : {outputs_dir}\")\n",
|
|
|
+ " return fichiers_trouves\n",
|
|
|
+ "\n",
|
|
|
+ " # Niveau 1 : années (ex: 2023, 2024, 2025)\n",
|
|
|
+ " for dossier_annee in sorted(outputs_dir.iterdir()):\n",
|
|
|
+ " if not dossier_annee.is_dir():\n",
|
|
|
+ " continue\n",
|
|
|
+ " annee = dossier_annee.name.strip()\n",
|
|
|
+ "\n",
|
|
|
+ " # On garde uniquement les dossiers dont le nom est une année (4 chiffres)\n",
|
|
|
+ " if not re.fullmatch(r\"\\d{4}\", annee):\n",
|
|
|
+ " log.debug(f\"Dossier ignoré (pas une année) : {dossier_annee.name}\")\n",
|
|
|
+ " continue\n",
|
|
|
+ "\n",
|
|
|
+ " # Niveau 2 : entités (ex: ACM_Vie_SA, BNP_Cardif)\n",
|
|
|
+ " for dossier_entite in sorted(dossier_annee.iterdir()):\n",
|
|
|
+ " if not dossier_entite.is_dir():\n",
|
|
|
+ " continue\n",
|
|
|
+ " entite = dossier_entite.name\n",
|
|
|
+ "\n",
|
|
|
+ " # ── Nettoyage pour le match ──────────────────────────────────────\n",
|
|
|
+ " # Dossier : ACM_Vie_SA → remplace _ par espace → minuscules\n",
|
|
|
+ " entite_clean = entite.replace(\"_\", \" \").strip().lower()\n",
|
|
|
+ "\n",
|
|
|
+ " # Recherche dans le fichier de paramétrage\n",
|
|
|
+ " df_filtre = df_param[\n",
|
|
|
+ " (df_param[\"entite_clean\"] == entite_clean) &\n",
|
|
|
+ " (df_param[\"Année\"] == annee)\n",
|
|
|
+ " ]\n",
|
|
|
+ "\n",
|
|
|
+ " if df_filtre.empty:\n",
|
|
|
+ " log.warning(\n",
|
|
|
+ " f\" ❌ NO MATCH paramétrage → entite='{entite_clean}' | annee='{annee}'\"\n",
|
|
|
+ " )\n",
|
|
|
+ " unite = \"N/A\" # valeur par défaut si pas trouvé\n",
|
|
|
+ " else:\n",
|
|
|
+ " # Extraction de la valeur scalaire (première ligne si doublons)\n",
|
|
|
+ " unite = str(df_filtre[\"Unité\"].iloc[0]).strip()\n",
|
|
|
+ "\n",
|
|
|
+ " # Niveau 3 : fichiers Excel\n",
|
|
|
+ " xlsx_trouves = list(dossier_entite.glob(\"*.xlsx\"))\n",
|
|
|
+ " if not xlsx_trouves:\n",
|
|
|
+ " log.warning(f\"Aucun .xlsx dans : {dossier_entite.relative_to(outputs_dir)}\")\n",
|
|
|
+ " continue\n",
|
|
|
+ "\n",
|
|
|
+ " for fichier in sorted(xlsx_trouves):\n",
|
|
|
+ " # On ignore le fichier de sortie s'il est dans le même dossier\n",
|
|
|
+ " if fichier.name == BASE_FINALE.name:\n",
|
|
|
+ " continue\n",
|
|
|
+ "\n",
|
|
|
+ " fichiers_trouves.append({\n",
|
|
|
+ " \"annee\": annee,\n",
|
|
|
+ " \"entite\": entite,\n",
|
|
|
+ " \"chemin\": fichier,\n",
|
|
|
+ " \"fichier\": fichier.name,\n",
|
|
|
+ " \"unite\": unite,\n",
|
|
|
+ " })\n",
|
|
|
+ "\n",
|
|
|
+ " log.info(f\"Étape 1 — {len(fichiers_trouves)} fichier(s) .xlsx trouvé(s)\")\n",
|
|
|
+ " return fichiers_trouves\n"
|
|
|
+ ]
|
|
|
+ },
|
|
|
+ {
|
|
|
+ "cell_type": "code",
|
|
|
+ "execution_count": 29,
|
|
|
+ "id": "6d39ed8a",
|
|
|
+ "metadata": {},
|
|
|
+ "outputs": [],
|
|
|
+ "source": [
|
|
|
+ "# =============================================================================\n",
|
|
|
+ "# ÉTAPE 2 — Parsing du nom de fichier → extraction de la section\n",
|
|
|
+ "# =============================================================================\n",
|
|
|
+ " \n",
|
|
|
+ "def extraire_section(nom_fichier: str) -> str | None:\n",
|
|
|
+ " \"\"\"\n",
|
|
|
+ " Extrait la section depuis le nom du fichier.\n",
|
|
|
+ " Ex: 'Rapport_S.02_page_56.xlsx' → 'S02'\n",
|
|
|
+ " Retourne None si le nom ne correspond pas au pattern.\n",
|
|
|
+ " \"\"\"\n",
|
|
|
+ " match = PATTERN_FICHIER.match(nom_fichier)\n",
|
|
|
+ " if match:\n",
|
|
|
+ " return f\"S{match.group(1)}\" # ex: '02' → 'S02'\n",
|
|
|
+ " return None"
|
|
|
+ ]
|
|
|
+ },
|
|
|
+ {
|
|
|
+ "cell_type": "code",
|
|
|
+ "execution_count": 30,
|
|
|
+ "id": "a23c3131",
|
|
|
+ "metadata": {},
|
|
|
+ "outputs": [],
|
|
|
+ "source": [
|
|
|
+ "# =============================================================================\n",
|
|
|
+ "# ÉTAPE 3 — Lecture et normalisation d'un fichier Excel\n",
|
|
|
+ "# =============================================================================\n",
|
|
|
+ " \n",
|
|
|
+ "def lire_fichier_excel(chemin: Path) -> pd.DataFrame | None:\n",
|
|
|
+ " \"\"\"\n",
|
|
|
+ " Lit un fichier QRT et retourne un DataFrame en format long :\n",
|
|
|
+ " R | C | valeur\n",
|
|
|
+ " La première ligne = en-têtes de colonnes C (ex: C0020, C0030…)\n",
|
|
|
+ " La première colonne = codes de lignes R (ex: R0010, R0020…)\n",
|
|
|
+ " \"\"\"\n",
|
|
|
+ " try:\n",
|
|
|
+ " # Lecture brute — header=0 : première ligne = noms de colonnes\n",
|
|
|
+ " df_brut = pd.read_excel(chemin, header=0, index_col=0, dtype=str)\n",
|
|
|
+ " \n",
|
|
|
+ " # Nettoyage des noms de lignes et colonnes\n",
|
|
|
+ " df_brut.index.name = \"R\"\n",
|
|
|
+ " df_brut.columns.name = \"C\"\n",
|
|
|
+ " \n",
|
|
|
+ " # Suppression des lignes/colonnes entièrement vides\n",
|
|
|
+ " df_brut.dropna(how=\"all\", inplace=True)\n",
|
|
|
+ " df_brut.dropna(axis=1, how=\"all\", inplace=True)\n",
|
|
|
+ " \n",
|
|
|
+ " # Filtrage : on garde uniquement les lignes dont l'index ressemble à un code R\n",
|
|
|
+ " masque_r = df_brut.index.str.match(r\"^R\\d{4}$\", na=False)\n",
|
|
|
+ " df_brut = df_brut[masque_r]\n",
|
|
|
+ " \n",
|
|
|
+ " # Filtrage : on garde uniquement les colonnes dont le nom ressemble à un code C\n",
|
|
|
+ " cols_c = [c for c in df_brut.columns if re.match(r\"^C\\d{4}$\", str(c))]\n",
|
|
|
+ " df_brut = df_brut[cols_c]\n",
|
|
|
+ " \n",
|
|
|
+ " if df_brut.empty:\n",
|
|
|
+ " log.warning(f\" Fichier vide après filtrage R/C : {chemin.name}\")\n",
|
|
|
+ " return None\n",
|
|
|
+ " \n",
|
|
|
+ " # Pivot en format long : une ligne par (R, C)\n",
|
|
|
+ " df_long = (\n",
|
|
|
+ " df_brut\n",
|
|
|
+ " .reset_index()\n",
|
|
|
+ " .melt(id_vars=\"R\", var_name=\"C\", value_name=\"valeur\")\n",
|
|
|
+ " )\n",
|
|
|
+ "\n",
|
|
|
+ " df_long[\"valeur\"] = (\n",
|
|
|
+ " df_long[\"valeur\"]\n",
|
|
|
+ " .astype(str)\n",
|
|
|
+ " .str.replace(\"\\u00a0\", \"\", regex=False) # espace insécable\n",
|
|
|
+ " .str.replace(\" \", \"\", regex=False) # espaces normaux\n",
|
|
|
+ " )\n",
|
|
|
+ "\n",
|
|
|
+ " \n",
|
|
|
+ " \n",
|
|
|
+ " # Conversion numérique de la colonne valeur\n",
|
|
|
+ " df_long[\"valeur\"] = pd.to_numeric(df_long[\"valeur\"], errors=\"coerce\")\n",
|
|
|
+ "\n",
|
|
|
+ "\n",
|
|
|
+ " # Ne pas garder les 0\n",
|
|
|
+ " df_long = df_long[df_long[\"valeur\"] != 0]\n",
|
|
|
+ " \n",
|
|
|
+ " # Suppression des lignes où la valeur est NaN\n",
|
|
|
+ " df_long.dropna(subset=[\"valeur\"], inplace=True)\n",
|
|
|
+ " \n",
|
|
|
+ " return df_long\n",
|
|
|
+ " \n",
|
|
|
+ " except Exception as e:\n",
|
|
|
+ " log.error(f\" Erreur lecture {chemin.name} : {e}\")\n",
|
|
|
+ " return None"
|
|
|
+ ]
|
|
|
+ },
|
|
|
+ {
|
|
|
+ "cell_type": "code",
|
|
|
+ "execution_count": 31,
|
|
|
+ "id": "b659cc7b",
|
|
|
+ "metadata": {},
|
|
|
+ "outputs": [],
|
|
|
+ "source": [
|
|
|
+ "# =============================================================================\n",
|
|
|
+ "# ÉTAPE 4 — Enrichissement et consolidation de tous les fichiers\n",
|
|
|
+ "# =============================================================================\n",
|
|
|
+ "\n",
|
|
|
+ "def consolider(fichiers: list[dict]) -> pd.DataFrame:\n",
|
|
|
+ " \"\"\"\n",
|
|
|
+ " Pour chaque fichier :\n",
|
|
|
+ " 1. Vérifie le nom (pattern QRT)\n",
|
|
|
+ " 2. Lit le contenu en format long\n",
|
|
|
+ " 3. Injecte les colonnes entite, annee, section, unite\n",
|
|
|
+ " 4. Calcule valeur_final (× 1000 si kEuros)\n",
|
|
|
+ " 5. Empile tout dans un grand DataFrame\n",
|
|
|
+ " \"\"\"\n",
|
|
|
+ " blocs = []\n",
|
|
|
+ " nb_ok = 0\n",
|
|
|
+ " nb_ignores = 0\n",
|
|
|
+ " nb_erreurs = 0\n",
|
|
|
+ "\n",
|
|
|
+ " for meta in fichiers:\n",
|
|
|
+ " annee = meta[\"annee\"]\n",
|
|
|
+ " entite = meta[\"entite\"]\n",
|
|
|
+ " chemin = meta[\"chemin\"]\n",
|
|
|
+ " fichier = meta[\"fichier\"]\n",
|
|
|
+ " unite = meta[\"unite\"]\n",
|
|
|
+ "\n",
|
|
|
+ " # --- Étape 2 intégrée : vérification du pattern ---\n",
|
|
|
+ " section = extraire_section(fichier)\n",
|
|
|
+ " if section is None:\n",
|
|
|
+ " log.warning(f\" Ignoré (nom non reconnu) : {fichier}\")\n",
|
|
|
+ " nb_ignores += 1\n",
|
|
|
+ " continue\n",
|
|
|
+ "\n",
|
|
|
+ " log.info(f\" Lecture : {annee}/{entite}/{fichier} → section {section}\")\n",
|
|
|
+ "\n",
|
|
|
+ " # --- Étape 3 : lecture ---\n",
|
|
|
+ " df = lire_fichier_excel(chemin)\n",
|
|
|
+ " if df is None:\n",
|
|
|
+ " nb_erreurs += 1\n",
|
|
|
+ " continue\n",
|
|
|
+ "\n",
|
|
|
+ " # --- Injection des métadonnées ---\n",
|
|
|
+ " df.insert(0, \"entite\", entite)\n",
|
|
|
+ " df.insert(1, \"annee\", int(annee))\n",
|
|
|
+ " df.insert(2, \"section\", section)\n",
|
|
|
+ " df.insert(3, \"unite\", unite)\n",
|
|
|
+ "\n",
|
|
|
+ " # --- Calcul valeur_final ---\n",
|
|
|
+ " # Si l'unité est kEuros (insensible à la casse), on multiplie par 1000\n",
|
|
|
+ " unite_lower = str(unite).lower().replace(\" \", \"\")\n",
|
|
|
+ " if unite_lower in (\"keuro\", \"keuros\", \"k€\", \"milliersd'euros\", \"keur\"):\n",
|
|
|
+ " df[\"valeur_final\"] = df[\"valeur\"] * 1000\n",
|
|
|
+ " elif unite_lower in (\"meuro\", \"meuros\", \"m€\", \"meur\"):\n",
|
|
|
+ " df[\"valeur_final\"] = df[\"valeur\"] * 1000000\n",
|
|
|
+ " else:\n",
|
|
|
+ " df[\"valeur_final\"] = df[\"valeur\"]\n",
|
|
|
+ " blocs.append(df)\n",
|
|
|
+ " nb_ok += 1\n",
|
|
|
+ "\n",
|
|
|
+ " log.info(f\"Étape 4 — {nb_ok} fichier(s) consolidé(s) | \"\n",
|
|
|
+ " f\"{nb_ignores} ignoré(s) | {nb_erreurs} erreur(s)\")\n",
|
|
|
+ "\n",
|
|
|
+ " if not blocs:\n",
|
|
|
+ " log.error(\"Aucun bloc à consolider. Vérifiez les fichiers sources.\")\n",
|
|
|
+ " return pd.DataFrame(columns=[\"entite\", \"annee\", \"section\", \"R\", \"C\",\n",
|
|
|
+ " \"valeur\", \"unite\", \"valeur_final\"])\n",
|
|
|
+ "\n",
|
|
|
+ " # Empilement de tous les blocs\n",
|
|
|
+ " df_final = pd.concat(blocs, ignore_index=True)\n",
|
|
|
+ "\n",
|
|
|
+ " # Ordre logique des colonnes\n",
|
|
|
+ " df_final = df_final[[\"entite\", \"annee\", \"section\", \"R\", \"C\",\n",
|
|
|
+ " \"valeur\", \"unite\", \"valeur_final\"]]\n",
|
|
|
+ "\n",
|
|
|
+ " # Tri pour lisibilité — on force les colonnes de tri en str pour éviter TypeError\n",
|
|
|
+ " df_final[\"annee\"] = df_final[\"annee\"].astype(int)\n",
|
|
|
+ " df_final[\"entite\"] = df_final[\"entite\"].astype(str)\n",
|
|
|
+ " df_final[\"section\"] = df_final[\"section\"].astype(str)\n",
|
|
|
+ " df_final[\"R\"] = df_final[\"R\"].astype(str)\n",
|
|
|
+ " df_final[\"C\"] = df_final[\"C\"].astype(str)\n",
|
|
|
+ "\n",
|
|
|
+ " df_final.sort_values(\n",
|
|
|
+ " by=[\"annee\", \"entite\", \"section\", \"R\", \"C\"],\n",
|
|
|
+ " inplace=True,\n",
|
|
|
+ " ignore_index=True,\n",
|
|
|
+ " )\n",
|
|
|
+ "\n",
|
|
|
+ " return df_final\n"
|
|
|
+ ]
|
|
|
+ },
|
|
|
+ {
|
|
|
+ "cell_type": "code",
|
|
|
+ "execution_count": 32,
|
|
|
+ "id": "8773eec3",
|
|
|
+ "metadata": {},
|
|
|
+ "outputs": [
|
|
|
+ {
|
|
|
+ "name": "stderr",
|
|
|
+ "output_type": "stream",
|
|
|
+ "text": [
|
|
|
+ "17:15:29 | INFO | === Démarrage consolidation QRT ===\n",
|
|
|
+ "17:15:29 | INFO | Dossier source : c:\\Users\\aiab\\Downloads\\Travaux\\SFCR_extraction_tool\\04 - Outputs\n",
|
|
|
+ "17:15:29 | INFO | Étape 1 — 213 fichier(s) .xlsx trouvé(s)\n",
|
|
|
+ "17:15:29 | INFO | Lecture : 2023/CNP_Assurances/Rapport_S.02_page_135.xlsx → section S02\n",
|
|
|
+ "17:15:29 | INFO | Lecture : 2023/CNP_Assurances/Rapport_S.02_page_136.xlsx → section S02\n",
|
|
|
+ "17:15:29 | INFO | Lecture : 2023/CNP_Assurances/Rapport_S.05_page_138.xlsx → section S05\n",
|
|
|
+ "17:15:29 | INFO | Lecture : 2023/Groupama_Gan_Vie/Rapport_S.19_page_78.xlsx → section S19\n",
|
|
|
+ "17:15:29 | INFO | Lecture : 2024/CNP_Assurances/Rapport_S.02_page_142.xlsx → section S02\n",
|
|
|
+ "17:15:29 | INFO | Lecture : 2024/CNP_Assurances/Rapport_S.02_page_143.xlsx → section S02\n",
|
|
|
+ "17:15:29 | INFO | Lecture : 2024/CNP_Assurances/Rapport_S.05_page_145.xlsx → section S05\n",
|
|
|
+ "17:15:29 | INFO | Lecture : 2024/CNP_Assurances/Rapport_S.05_page_146.xlsx → section S05\n",
|
|
|
+ "17:15:29 | INFO | Lecture : 2025/ACM_Vie_SA/Rapport_S.02_page_56.xlsx → section S02\n",
|
|
|
+ "17:15:29 | INFO | Lecture : 2025/ACM_Vie_SA/Rapport_S.05_page_57.xlsx → section S05\n",
|
|
|
+ "17:15:29 | INFO | Lecture : 2025/ACM_Vie_SA/Rapport_S.05_page_58.xlsx → section S05\n",
|
|
|
+ "17:15:29 | INFO | Lecture : 2025/ACM_Vie_SA/Rapport_S.12_page_59.xlsx → section S12\n",
|
|
|
+ "17:15:29 | INFO | Lecture : 2025/ACM_Vie_SA/Rapport_S.22_page_60.xlsx → section S22\n",
|
|
|
+ "17:15:29 | INFO | Lecture : 2025/ACM_Vie_SA/Rapport_S.23_page_61.xlsx → section S23\n",
|
|
|
+ "17:15:29 | INFO | Lecture : 2025/ACM_Vie_SA/Rapport_S.23_page_62.xlsx → section S23\n",
|
|
|
+ "17:15:29 | INFO | Lecture : 2025/ACM_Vie_SA/Rapport_S.25_page_63.xlsx → section S25\n",
|
|
|
+ "17:15:29 | INFO | Lecture : 2025/ACM_Vie_SA/Rapport_S.28_page_64.xlsx → section S28\n",
|
|
|
+ "17:15:29 | INFO | Lecture : 2025/ACM_Vie_SAM/Rapport_S.02_page_54.xlsx → section S02\n",
|
|
|
+ "17:15:29 | INFO | Lecture : 2025/ACM_Vie_SAM/Rapport_S.05_page_55.xlsx → section S05\n",
|
|
|
+ "17:15:29 | INFO | Lecture : 2025/ACM_Vie_SAM/Rapport_S.05_page_56.xlsx → section S05\n",
|
|
|
+ "17:15:29 | INFO | Lecture : 2025/ACM_Vie_SAM/Rapport_S.12_page_57.xlsx → section S12\n",
|
|
|
+ "17:15:29 | INFO | Lecture : 2025/ACM_Vie_SAM/Rapport_S.22_page_58.xlsx → section S22\n",
|
|
|
+ "17:15:29 | INFO | Lecture : 2025/ACM_Vie_SAM/Rapport_S.23_page_59.xlsx → section S23\n",
|
|
|
+ "17:15:29 | INFO | Lecture : 2025/ACM_Vie_SAM/Rapport_S.23_page_60.xlsx → section S23\n",
|
|
|
+ "17:15:29 | INFO | Lecture : 2025/ACM_Vie_SAM/Rapport_S.25_page_61.xlsx → section S25\n",
|
|
|
+ "17:15:29 | INFO | Lecture : 2025/ACM_Vie_SAM/Rapport_S.28_page_62.xlsx → section S28\n",
|
|
|
+ "17:15:29 | INFO | Lecture : 2025/Allianz_Vie/Rapport_S.02_page_81.xlsx → section S02\n",
|
|
|
+ "17:15:29 | INFO | Lecture : 2025/Allianz_Vie/Rapport_S.02_page_82.xlsx → section S02\n",
|
|
|
+ "17:15:29 | INFO | Lecture : 2025/Allianz_Vie/Rapport_S.05_page_84.xlsx → section S05\n",
|
|
|
+ "17:15:30 | WARNING | Fichier vide après filtrage R/C : Rapport_S.05_page_84.xlsx\n",
|
|
|
+ "17:15:30 | INFO | Lecture : 2025/Allianz_Vie/Rapport_S.12_page_85.xlsx → section S12\n",
|
|
|
+ "17:15:30 | INFO | Lecture : 2025/Allianz_Vie/Rapport_S.12_page_86.xlsx → section S12\n",
|
|
|
+ "17:15:30 | INFO | Lecture : 2025/Allianz_Vie/Rapport_S.22_page_87.xlsx → section S22\n",
|
|
|
+ "17:15:30 | INFO | Lecture : 2025/Allianz_Vie/Rapport_S.23_page_88.xlsx → section S23\n",
|
|
|
+ "17:15:30 | INFO | Lecture : 2025/Allianz_Vie/Rapport_S.23_page_89.xlsx → section S23\n",
|
|
|
+ "17:15:30 | INFO | Lecture : 2025/Allianz_Vie/Rapport_S.25_page_90.xlsx → section S25\n",
|
|
|
+ "17:15:30 | INFO | Lecture : 2025/Allianz_Vie/Rapport_S.25_page_91.xlsx → section S25\n",
|
|
|
+ "17:15:30 | INFO | Lecture : 2025/Allianz_Vie/Rapport_S.28_page_92.xlsx → section S28\n",
|
|
|
+ "17:15:30 | INFO | Lecture : 2025/Caisse_Générale_de_Prévoyance_(CGP)/Rapport_S.02_page_48.xlsx → section S02\n",
|
|
|
+ "17:15:30 | INFO | Lecture : 2025/Caisse_Générale_de_Prévoyance_(CGP)/Rapport_S.02_page_49.xlsx → section S02\n",
|
|
|
+ "17:15:30 | INFO | Lecture : 2025/Caisse_Générale_de_Prévoyance_(CGP)/Rapport_S.05_page_50.xlsx → section S05\n",
|
|
|
+ "17:15:30 | INFO | Lecture : 2025/Caisse_Générale_de_Prévoyance_(CGP)/Rapport_S.05_page_51.xlsx → section S05\n",
|
|
|
+ "17:15:30 | INFO | Lecture : 2025/Caisse_Générale_de_Prévoyance_(CGP)/Rapport_S.12_page_52.xlsx → section S12\n",
|
|
|
+ "17:15:30 | INFO | Lecture : 2025/Caisse_Générale_de_Prévoyance_(CGP)/Rapport_S.22_page_55.xlsx → section S22\n",
|
|
|
+ "17:15:30 | INFO | Lecture : 2025/Caisse_Générale_de_Prévoyance_(CGP)/Rapport_S.23_page_56.xlsx → section S23\n",
|
|
|
+ "17:15:30 | INFO | Lecture : 2025/Caisse_Générale_de_Prévoyance_(CGP)/Rapport_S.23_page_57.xlsx → section S23\n",
|
|
|
+ "17:15:30 | INFO | Lecture : 2025/Caisse_Générale_de_Prévoyance_(CGP)/Rapport_S.25_page_58.xlsx → section S25\n",
|
|
|
+ "17:15:30 | INFO | Lecture : 2025/Caisse_Générale_de_Prévoyance_(CGP)/Rapport_S.28_page_59.xlsx → section S28\n",
|
|
|
+ "17:15:30 | WARNING | Ignoré (nom non reconnu) : ~$Rapport_S.28_page_59.xlsx\n",
|
|
|
+ "17:15:30 | INFO | Lecture : 2025/Cardif_Assurance_Vie/Rapport_S.02_page_2.xlsx → section S02\n",
|
|
|
+ "17:15:30 | INFO | Lecture : 2025/Cardif_Assurance_Vie/Rapport_S.02_page_3.xlsx → section S02\n",
|
|
|
+ "17:15:30 | INFO | Lecture : 2025/Cardif_Assurance_Vie/Rapport_S.05_page_6.xlsx → section S05\n",
|
|
|
+ "17:15:30 | INFO | Lecture : 2025/Cardif_Assurance_Vie/Rapport_S.12_page_7.xlsx → section S12\n",
|
|
|
+ "17:15:30 | INFO | Lecture : 2025/Cardif_Assurance_Vie/Rapport_S.22_page_11.xlsx → section S22\n",
|
|
|
+ "17:15:30 | INFO | Lecture : 2025/Cardif_Assurance_Vie/Rapport_S.23_page_12.xlsx → section S23\n",
|
|
|
+ "17:15:30 | INFO | Lecture : 2025/Cardif_Assurance_Vie/Rapport_S.25_page_13.xlsx → section S25\n",
|
|
|
+ "17:15:30 | INFO | Lecture : 2025/Cardif_Assurance_Vie/Rapport_S.28_page_14.xlsx → section S28\n",
|
|
|
+ "17:15:30 | INFO | Lecture : 2025/CNP_Assurances/Rapport_S.02_page_83.xlsx → section S02\n",
|
|
|
+ "17:15:30 | WARNING | Fichier vide après filtrage R/C : Rapport_S.02_page_83.xlsx\n",
|
|
|
+ "17:15:30 | INFO | Lecture : 2025/CNP_Assurances/Rapport_S.02_page_84.xlsx → section S02\n",
|
|
|
+ "17:15:30 | WARNING | Fichier vide après filtrage R/C : Rapport_S.02_page_84.xlsx\n",
|
|
|
+ "17:15:30 | INFO | Lecture : 2025/CNP_Assurances/Rapport_S.05_page_86.xlsx → section S05\n",
|
|
|
+ "17:15:30 | INFO | Lecture : 2025/CNP_Assurances/Rapport_S.05_page_87.xlsx → section S05\n",
|
|
|
+ "17:15:30 | INFO | Lecture : 2025/CNP_Assurances/Rapport_S.12_page_88.xlsx → section S12\n",
|
|
|
+ "17:15:30 | INFO | Lecture : 2025/CNP_Assurances/Rapport_S.12_page_89.xlsx → section S12\n",
|
|
|
+ "17:15:30 | WARNING | Fichier vide après filtrage R/C : Rapport_S.12_page_89.xlsx\n",
|
|
|
+ "17:15:30 | INFO | Lecture : 2025/CNP_Assurances/Rapport_S.12_page_90.xlsx → section S12\n",
|
|
|
+ "17:15:30 | INFO | Lecture : 2025/CNP_Assurances/Rapport_S.17_page_91.xlsx → section S17\n",
|
|
|
+ "17:15:30 | INFO | Lecture : 2025/CNP_Assurances/Rapport_S.19_page_92.xlsx → section S19\n",
|
|
|
+ "17:15:30 | INFO | Lecture : 2025/CNP_Assurances/Rapport_S.19_page_93.xlsx → section S19\n",
|
|
|
+ "17:15:30 | INFO | Lecture : 2025/CNP_Assurances/Rapport_S.22_page_94.xlsx → section S22\n",
|
|
|
+ "17:15:30 | INFO | Lecture : 2025/CNP_Assurances/Rapport_S.23_page_95.xlsx → section S23\n",
|
|
|
+ "17:15:30 | INFO | Lecture : 2025/CNP_Assurances/Rapport_S.23_page_96.xlsx → section S23\n",
|
|
|
+ "17:15:30 | INFO | Lecture : 2025/CNP_Assurances/Rapport_S.25_page_97.xlsx → section S25\n",
|
|
|
+ "17:15:30 | INFO | Lecture : 2025/CNP_Assurances/Rapport_S.28_page_98.xlsx → section S28\n",
|
|
|
+ "17:15:30 | INFO | Lecture : 2025/CNP_Assurances/Rapport_S.28_page_99.xlsx → section S28\n",
|
|
|
+ "17:15:30 | INFO | Lecture : 2025/GMF_Vie/Rapport_S.02_page_57.xlsx → section S02\n",
|
|
|
+ "17:15:30 | INFO | Lecture : 2025/GMF_Vie/Rapport_S.02_page_58.xlsx → section S02\n",
|
|
|
+ "17:15:30 | INFO | Lecture : 2025/GMF_Vie/Rapport_S.05_page_59.xlsx → section S05\n",
|
|
|
+ "17:15:30 | INFO | Lecture : 2025/GMF_Vie/Rapport_S.12_page_60.xlsx → section S12\n",
|
|
|
+ "17:15:30 | INFO | Lecture : 2025/GMF_Vie/Rapport_S.22_page_62.xlsx → section S22\n",
|
|
|
+ "17:15:30 | INFO | Lecture : 2025/GMF_Vie/Rapport_S.23_page_63.xlsx → section S23\n",
|
|
|
+ "17:15:30 | INFO | Lecture : 2025/GMF_Vie/Rapport_S.23_page_64.xlsx → section S23\n",
|
|
|
+ "17:15:30 | INFO | Lecture : 2025/GMF_Vie/Rapport_S.25_page_65.xlsx → section S25\n",
|
|
|
+ "17:15:30 | INFO | Lecture : 2025/GMF_Vie/Rapport_S.25_page_66.xlsx → section S25\n",
|
|
|
+ "17:15:30 | INFO | Lecture : 2025/Groupama_Gan_Vie/Rapport_S.02_page_71.xlsx → section S02\n",
|
|
|
+ "17:15:30 | INFO | Lecture : 2025/Groupama_Gan_Vie/Rapport_S.02_page_72.xlsx → section S02\n",
|
|
|
+ "17:15:30 | INFO | Lecture : 2025/Groupama_Gan_Vie/Rapport_S.05_page_73.xlsx → section S05\n",
|
|
|
+ "17:15:30 | INFO | Lecture : 2025/Groupama_Gan_Vie/Rapport_S.05_page_74.xlsx → section S05\n",
|
|
|
+ "17:15:30 | INFO | Lecture : 2025/Groupama_Gan_Vie/Rapport_S.12_page_75.xlsx → section S12\n",
|
|
|
+ "17:15:30 | INFO | Lecture : 2025/Groupama_Gan_Vie/Rapport_S.22_page_78.xlsx → section S22\n",
|
|
|
+ "17:15:30 | INFO | Lecture : 2025/Groupama_Gan_Vie/Rapport_S.23_page_80.xlsx → section S23\n",
|
|
|
+ "17:15:30 | INFO | Lecture : 2025/Groupama_Gan_Vie/Rapport_S.25_page_81.xlsx → section S25\n",
|
|
|
+ "17:15:30 | INFO | Lecture : 2025/Groupama_Gan_Vie/Rapport_S.28_page_82.xlsx → section S28\n",
|
|
|
+ "17:15:30 | INFO | Lecture : 2025/HSBC_Assurances_Vie/Rapport_S.02_page_76.xlsx → section S02\n",
|
|
|
+ "17:15:30 | INFO | Lecture : 2025/HSBC_Assurances_Vie/Rapport_S.02_page_77.xlsx → section S02\n",
|
|
|
+ "17:15:30 | WARNING | Fichier vide après filtrage R/C : Rapport_S.02_page_77.xlsx\n",
|
|
|
+ "17:15:30 | INFO | Lecture : 2025/HSBC_Assurances_Vie/Rapport_S.05_page_78.xlsx → section S05\n",
|
|
|
+ "17:15:30 | INFO | Lecture : 2025/HSBC_Assurances_Vie/Rapport_S.12_page_79.xlsx → section S12\n",
|
|
|
+ "17:15:30 | INFO | Lecture : 2025/HSBC_Assurances_Vie/Rapport_S.22_page_80.xlsx → section S22\n",
|
|
|
+ "17:15:30 | INFO | Lecture : 2025/HSBC_Assurances_Vie/Rapport_S.23_page_81.xlsx → section S23\n",
|
|
|
+ "17:15:30 | INFO | Lecture : 2025/HSBC_Assurances_Vie/Rapport_S.25_page_82.xlsx → section S25\n",
|
|
|
+ "17:15:30 | INFO | Lecture : 2025/HSBC_Assurances_Vie/Rapport_S.28_page_83.xlsx → section S28\n",
|
|
|
+ "17:15:30 | INFO | Lecture : 2025/La_Mondiale_/Rapport_S.02_page_122.xlsx → section S02\n",
|
|
|
+ "17:15:30 | INFO | Lecture : 2025/La_Mondiale_/Rapport_S.02_page_123.xlsx → section S02\n",
|
|
|
+ "17:15:30 | INFO | Lecture : 2025/La_Mondiale_/Rapport_S.05_page_124.xlsx → section S05\n",
|
|
|
+ "17:15:30 | INFO | Lecture : 2025/La_Mondiale_/Rapport_S.05_page_125.xlsx → section S05\n",
|
|
|
+ "17:15:30 | INFO | Lecture : 2025/La_Mondiale_/Rapport_S.05_page_126.xlsx → section S05\n",
|
|
|
+ "17:15:30 | INFO | Lecture : 2025/La_Mondiale_/Rapport_S.05_page_127.xlsx → section S05\n",
|
|
|
+ "17:15:30 | INFO | Lecture : 2025/La_Mondiale_/Rapport_S.22_page_128.xlsx → section S22\n",
|
|
|
+ "17:15:30 | INFO | Lecture : 2025/La_Mondiale_/Rapport_S.23_page_129.xlsx → section S23\n",
|
|
|
+ "17:15:30 | INFO | Lecture : 2025/La_Mondiale_/Rapport_S.23_page_130.xlsx → section S23\n",
|
|
|
+ "17:15:30 | INFO | Lecture : 2025/La_Mondiale_/Rapport_S.23_page_131.xlsx → section S23\n",
|
|
|
+ "17:15:30 | INFO | Lecture : 2025/La_Mondiale_/Rapport_S.23_page_132.xlsx → section S23\n",
|
|
|
+ "17:15:30 | INFO | Lecture : 2025/La_Mondiale_/Rapport_S.25_page_133.xlsx → section S25\n",
|
|
|
+ "17:15:30 | INFO | Lecture : 2025/La_Mondiale_/Rapport_S.25_page_134.xlsx → section S25\n",
|
|
|
+ "17:15:30 | INFO | Lecture : 2025/La_Mondiale_/Rapport_S.28_page_253.xlsx → section S28\n",
|
|
|
+ "17:15:30 | INFO | Lecture : 2025/La_Mondiale_/Rapport_S.28_page_254.xlsx → section S28\n",
|
|
|
+ "17:15:30 | INFO | Lecture : 2025/La_Mondiale_Partenaire/Rapport_S.02_page_273.xlsx → section S02\n",
|
|
|
+ "17:15:30 | INFO | Lecture : 2025/La_Mondiale_Partenaire/Rapport_S.02_page_274.xlsx → section S02\n",
|
|
|
+ "17:15:30 | INFO | Lecture : 2025/La_Mondiale_Partenaire/Rapport_S.05_page_275.xlsx → section S05\n",
|
|
|
+ "17:15:30 | INFO | Lecture : 2025/La_Mondiale_Partenaire/Rapport_S.05_page_276.xlsx → section S05\n",
|
|
|
+ "17:15:30 | INFO | Lecture : 2025/La_Mondiale_Partenaire/Rapport_S.12_page_277.xlsx → section S12\n",
|
|
|
+ "17:15:30 | INFO | Lecture : 2025/La_Mondiale_Partenaire/Rapport_S.12_page_278.xlsx → section S12\n",
|
|
|
+ "17:15:30 | INFO | Lecture : 2025/La_Mondiale_Partenaire/Rapport_S.22_page_279.xlsx → section S22\n",
|
|
|
+ "17:15:30 | INFO | Lecture : 2025/La_Mondiale_Partenaire/Rapport_S.23_page_280.xlsx → section S23\n",
|
|
|
+ "17:15:30 | INFO | Lecture : 2025/La_Mondiale_Partenaire/Rapport_S.23_page_281.xlsx → section S23\n",
|
|
|
+ "17:15:30 | INFO | Lecture : 2025/La_Mondiale_Partenaire/Rapport_S.23_page_282.xlsx → section S23\n",
|
|
|
+ "17:15:30 | INFO | Lecture : 2025/La_Mondiale_Partenaire/Rapport_S.25_page_283.xlsx → section S25\n",
|
|
|
+ "17:15:30 | INFO | Lecture : 2025/La_Mondiale_Partenaire/Rapport_S.25_page_284.xlsx → section S25\n",
|
|
|
+ "17:15:30 | INFO | Lecture : 2025/La_Mondiale_Partenaire/Rapport_S.28_page_285.xlsx → section S28\n",
|
|
|
+ "17:15:30 | INFO | Lecture : 2025/La_Mondiale_Partenaire/Rapport_S.28_page_286.xlsx → section S28\n",
|
|
|
+ "17:15:30 | INFO | Lecture : 2025/MACSF_Epargne_Retraite/Rapport_S.02_page_57.xlsx → section S02\n",
|
|
|
+ "17:15:30 | INFO | Lecture : 2025/MACSF_Epargne_Retraite/Rapport_S.02_page_58.xlsx → section S02\n",
|
|
|
+ "17:15:30 | WARNING | Fichier vide après filtrage R/C : Rapport_S.02_page_58.xlsx\n",
|
|
|
+ "17:15:30 | INFO | Lecture : 2025/MACSF_Epargne_Retraite/Rapport_S.05_page_59.xlsx → section S05\n",
|
|
|
+ "17:15:31 | INFO | Lecture : 2025/MACSF_Epargne_Retraite/Rapport_S.05_page_60.xlsx → section S05\n",
|
|
|
+ "17:15:31 | INFO | Lecture : 2025/MACSF_Epargne_Retraite/Rapport_S.05_page_61.xlsx → section S05\n",
|
|
|
+ "17:15:31 | INFO | Lecture : 2025/MACSF_Epargne_Retraite/Rapport_S.12_page_62.xlsx → section S12\n",
|
|
|
+ "17:15:31 | INFO | Lecture : 2025/MACSF_Epargne_Retraite/Rapport_S.12_page_63.xlsx → section S12\n",
|
|
|
+ "17:15:31 | INFO | Lecture : 2025/MACSF_Epargne_Retraite/Rapport_S.22_page_64.xlsx → section S22\n",
|
|
|
+ "17:15:31 | INFO | Lecture : 2025/MACSF_Epargne_Retraite/Rapport_S.23_page_65.xlsx → section S23\n",
|
|
|
+ "17:15:31 | INFO | Lecture : 2025/MACSF_Epargne_Retraite/Rapport_S.25_page_66.xlsx → section S25\n",
|
|
|
+ "17:15:31 | INFO | Lecture : 2025/MACSF_Epargne_Retraite/Rapport_S.28_page_67.xlsx → section S28\n",
|
|
|
+ "17:15:31 | WARNING | Ignoré (nom non reconnu) : ~$Rapport_S.02_page_58.xlsx\n",
|
|
|
+ "17:15:31 | INFO | Lecture : 2025/MAIF_Vie/Rapport_S.02_page_103.xlsx → section S02\n",
|
|
|
+ "17:15:31 | INFO | Lecture : 2025/MAIF_Vie/Rapport_S.02_page_104.xlsx → section S02\n",
|
|
|
+ "17:15:31 | INFO | Lecture : 2025/MAIF_Vie/Rapport_S.05_page_105.xlsx → section S05\n",
|
|
|
+ "17:15:31 | ERROR | Erreur lecture Rapport_S.05_page_105.xlsx : Can only use .str accessor with string values, not integer\n",
|
|
|
+ "17:15:31 | INFO | Lecture : 2025/MAIF_Vie/Rapport_S.05_page_106.xlsx → section S05\n",
|
|
|
+ "17:15:31 | ERROR | Erreur lecture Rapport_S.05_page_106.xlsx : Can only use .str accessor with string values, not integer\n",
|
|
|
+ "17:15:31 | INFO | Lecture : 2025/MAIF_Vie/Rapport_S.05_page_107.xlsx → section S05\n",
|
|
|
+ "17:15:31 | INFO | Lecture : 2025/MAIF_Vie/Rapport_S.12_page_108.xlsx → section S12\n",
|
|
|
+ "17:15:31 | INFO | Lecture : 2025/MAIF_Vie/Rapport_S.22_page_109.xlsx → section S22\n",
|
|
|
+ "17:15:31 | INFO | Lecture : 2025/MAIF_Vie/Rapport_S.23_page_110.xlsx → section S23\n",
|
|
|
+ "17:15:31 | INFO | Lecture : 2025/MAIF_Vie/Rapport_S.23_page_111.xlsx → section S23\n",
|
|
|
+ "17:15:31 | INFO | Lecture : 2025/MAIF_Vie/Rapport_S.25_page_112.xlsx → section S25\n",
|
|
|
+ "17:15:31 | INFO | Lecture : 2025/MAIF_Vie/Rapport_S.28_page_114.xlsx → section S28\n",
|
|
|
+ "17:15:31 | INFO | Lecture : 2025/PREDICA/Rapport_S.02_page_52.xlsx → section S02\n",
|
|
|
+ "17:15:31 | INFO | Lecture : 2025/PREDICA/Rapport_S.02_page_53.xlsx → section S02\n",
|
|
|
+ "17:15:31 | INFO | Lecture : 2025/PREDICA/Rapport_S.05_page_55.xlsx → section S05\n",
|
|
|
+ "17:15:31 | INFO | Lecture : 2025/PREDICA/Rapport_S.05_page_56.xlsx → section S05\n",
|
|
|
+ "17:15:31 | INFO | Lecture : 2025/PREDICA/Rapport_S.05_page_57.xlsx → section S05\n",
|
|
|
+ "17:15:31 | INFO | Lecture : 2025/PREDICA/Rapport_S.05_page_58.xlsx → section S05\n",
|
|
|
+ "17:15:31 | INFO | Lecture : 2025/PREDICA/Rapport_S.05_page_59.xlsx → section S05\n",
|
|
|
+ "17:15:31 | INFO | Lecture : 2025/PREDICA/Rapport_S.12_page_60.xlsx → section S12\n",
|
|
|
+ "17:15:31 | INFO | Lecture : 2025/PREDICA/Rapport_S.12_page_61.xlsx → section S12\n",
|
|
|
+ "17:15:31 | INFO | Lecture : 2025/PREDICA/Rapport_S.22_page_67.xlsx → section S22\n",
|
|
|
+ "17:15:31 | INFO | Lecture : 2025/PREDICA/Rapport_S.23_page_68.xlsx → section S23\n",
|
|
|
+ "17:15:31 | INFO | Lecture : 2025/PREDICA/Rapport_S.23_page_70.xlsx → section S23\n",
|
|
|
+ "17:15:31 | INFO | Lecture : 2025/PREDICA/Rapport_S.25_page_71.xlsx → section S25\n",
|
|
|
+ "17:15:31 | INFO | Lecture : 2025/PREDICA/Rapport_S.28_page_72.xlsx → section S28\n",
|
|
|
+ "17:15:31 | INFO | Lecture : 2025/PREDICA/Rapport_S.28_page_73.xlsx → section S28\n",
|
|
|
+ "17:15:31 | INFO | Lecture : 2025/SOGECAP/Rapport_S.02_page_32.xlsx → section S02\n",
|
|
|
+ "17:15:31 | INFO | Lecture : 2025/SOGECAP/Rapport_S.05_page_33.xlsx → section S05\n",
|
|
|
+ "17:15:31 | INFO | Lecture : 2025/SOGECAP/Rapport_S.05_page_34.xlsx → section S05\n",
|
|
|
+ "17:15:31 | INFO | Lecture : 2025/SOGECAP/Rapport_S.12_page_36.xlsx → section S12\n",
|
|
|
+ "17:15:31 | INFO | Lecture : 2025/SOGECAP/Rapport_S.22_page_39.xlsx → section S22\n",
|
|
|
+ "17:15:31 | INFO | Lecture : 2025/SOGECAP/Rapport_S.23_page_39.xlsx → section S23\n",
|
|
|
+ "17:15:31 | INFO | Lecture : 2025/SOGECAP/Rapport_S.23_page_40.xlsx → section S23\n",
|
|
|
+ "17:15:31 | INFO | Lecture : 2025/SOGECAP/Rapport_S.25_page_40.xlsx → section S25\n",
|
|
|
+ "17:15:31 | INFO | Lecture : 2025/SOGECAP/Rapport_S.28_page_41.xlsx → section S28\n",
|
|
|
+ "17:15:31 | INFO | Lecture : 2025/SPIRICA/Rapport_S.02_page_54.xlsx → section S02\n",
|
|
|
+ "17:15:31 | INFO | Lecture : 2025/SPIRICA/Rapport_S.02_page_55.xlsx → section S02\n",
|
|
|
+ "17:15:31 | INFO | Lecture : 2025/SPIRICA/Rapport_S.05_page_56.xlsx → section S05\n",
|
|
|
+ "17:15:31 | INFO | Lecture : 2025/SPIRICA/Rapport_S.05_page_57.xlsx → section S05\n",
|
|
|
+ "17:15:31 | INFO | Lecture : 2025/SPIRICA/Rapport_S.12_page_58.xlsx → section S12\n",
|
|
|
+ "17:15:31 | INFO | Lecture : 2025/SPIRICA/Rapport_S.12_page_59.xlsx → section S12\n",
|
|
|
+ "17:15:31 | INFO | Lecture : 2025/SPIRICA/Rapport_S.22_page_60.xlsx → section S22\n",
|
|
|
+ "17:15:31 | INFO | Lecture : 2025/SPIRICA/Rapport_S.23_page_61.xlsx → section S23\n",
|
|
|
+ "17:15:31 | INFO | Lecture : 2025/SPIRICA/Rapport_S.23_page_62.xlsx → section S23\n",
|
|
|
+ "17:15:31 | INFO | Lecture : 2025/SPIRICA/Rapport_S.25_page_63.xlsx → section S25\n",
|
|
|
+ "17:15:31 | INFO | Lecture : 2025/SPIRICA/Rapport_S.25_page_64.xlsx → section S25\n",
|
|
|
+ "17:15:31 | WARNING | Fichier vide après filtrage R/C : Rapport_S.25_page_64.xlsx\n",
|
|
|
+ "17:15:31 | INFO | Lecture : 2025/SPIRICA/Rapport_S.28_page_65.xlsx → section S28\n",
|
|
|
+ "17:15:31 | INFO | Lecture : 2025/SPIRICA/Rapport_S.28_page_66.xlsx → section S28\n",
|
|
|
+ "17:15:31 | WARNING | Ignoré (nom non reconnu) : ~$Rapport_S.25_page_64.xlsx\n",
|
|
|
+ "17:15:31 | INFO | Lecture : 2025/SURAVENIR/Rapport_S.02_page_70.xlsx → section S02\n",
|
|
|
+ "17:15:31 | INFO | Lecture : 2025/SURAVENIR/Rapport_S.02_page_71.xlsx → section S02\n",
|
|
|
+ "17:15:31 | INFO | Lecture : 2025/SURAVENIR/Rapport_S.05_page_72.xlsx → section S05\n",
|
|
|
+ "17:15:31 | INFO | Lecture : 2025/SURAVENIR/Rapport_S.12_page_73.xlsx → section S12\n",
|
|
|
+ "17:15:31 | INFO | Lecture : 2025/SURAVENIR/Rapport_S.22_page_74.xlsx → section S22\n",
|
|
|
+ "17:15:31 | INFO | Lecture : 2025/SURAVENIR/Rapport_S.23_page_75.xlsx → section S23\n",
|
|
|
+ "17:15:31 | INFO | Lecture : 2025/SURAVENIR/Rapport_S.25_page_76.xlsx → section S25\n",
|
|
|
+ "17:15:31 | INFO | Lecture : 2025/SURAVENIR/Rapport_S.28_page_77.xlsx → section S28\n",
|
|
|
+ "17:15:31 | INFO | Lecture : 2025/SURAVENIR_SA/Rapport_S.02_page_72.xlsx → section S02\n",
|
|
|
+ "17:15:31 | INFO | Lecture : 2025/SURAVENIR_SA/Rapport_S.02_page_73.xlsx → section S02\n",
|
|
|
+ "17:15:31 | INFO | Lecture : 2025/SURAVENIR_SA/Rapport_S.05_page_74.xlsx → section S05\n",
|
|
|
+ "17:15:31 | INFO | Lecture : 2025/SURAVENIR_SA/Rapport_S.12_page_75.xlsx → section S12\n",
|
|
|
+ "17:15:31 | INFO | Lecture : 2025/SURAVENIR_SA/Rapport_S.23_page_77.xlsx → section S23\n",
|
|
|
+ "17:15:31 | INFO | Lecture : 2025/SURAVENIR_SA/Rapport_S.23_page_78.xlsx → section S23\n",
|
|
|
+ "17:15:31 | INFO | Lecture : 2025/SURAVENIR_SA/Rapport_S.25_page_79.xlsx → section S25\n",
|
|
|
+ "17:15:31 | INFO | Lecture : 2025/SURAVENIR_SA/Rapport_S.28_page_80.xlsx → section S28\n",
|
|
|
+ "17:15:31 | INFO | Lecture : 2025/SURAVENIR_SA/Rapport_S.28_page_81.xlsx → section S28\n",
|
|
|
+ "17:15:31 | INFO | Lecture : 2025/SwissLife_Assurance_et_Patrimoine/Rapport_S.02_page_100.xlsx → section S02\n",
|
|
|
+ "17:15:31 | INFO | Lecture : 2025/SwissLife_Assurance_et_Patrimoine/Rapport_S.02_page_99.xlsx → section S02\n",
|
|
|
+ "17:15:31 | INFO | Lecture : 2025/SwissLife_Assurance_et_Patrimoine/Rapport_S.05_page_101.xlsx → section S05\n",
|
|
|
+ "17:15:31 | INFO | Lecture : 2025/SwissLife_Assurance_et_Patrimoine/Rapport_S.12_page_102.xlsx → section S12\n",
|
|
|
+ "17:15:31 | INFO | Lecture : 2025/SwissLife_Assurance_et_Patrimoine/Rapport_S.12_page_103.xlsx → section S12\n",
|
|
|
+ "17:15:31 | INFO | Lecture : 2025/SwissLife_Assurance_et_Patrimoine/Rapport_S.22_page_104.xlsx → section S22\n",
|
|
|
+ "17:15:31 | INFO | Lecture : 2025/SwissLife_Assurance_et_Patrimoine/Rapport_S.25_page_106.xlsx → section S25\n",
|
|
|
+ "17:15:31 | INFO | Lecture : 2025/SwissLife_Assurance_et_Patrimoine/Rapport_S.28_page_107.xlsx → section S28\n",
|
|
|
+ "17:15:31 | INFO | Étape 4 — 201 fichier(s) consolidé(s) | 3 ignoré(s) | 9 erreur(s)\n",
|
|
|
+ "17:15:32 | INFO | ============================================================\n",
|
|
|
+ "17:15:32 | INFO | RÉSUMÉ DE LA CONSOLIDATION\n",
|
|
|
+ "17:15:32 | INFO | Fichier exporté : base_consolidee_QRT.xlsx\n",
|
|
|
+ "17:15:32 | INFO | Lignes totales : 4,740\n",
|
|
|
+ "17:15:32 | INFO | Entités : ['ACM_Vie_SA', 'ACM_Vie_SAM', 'Allianz_Vie', 'CNP_Assurances', 'Caisse_Générale_de_Prévoyance_(CGP)', 'Cardif_Assurance_Vie', 'GMF_Vie', 'Groupama_Gan_Vie', 'HSBC_Assurances_Vie', 'La_Mondiale_', 'La_Mondiale_Partenaire', 'MACSF_Epargne_Retraite', 'MAIF_Vie', 'PREDICA', 'SOGECAP', 'SPIRICA', 'SURAVENIR', 'SURAVENIR_SA', 'SwissLife_Assurance_et_Patrimoine']\n",
|
|
|
+ "17:15:32 | INFO | Années : [2023, 2024, 2025]\n",
|
|
|
+ "17:15:32 | INFO | Sections : ['S02', 'S05', 'S12', 'S17', 'S19', 'S22', 'S23', 'S25', 'S28']\n",
|
|
|
+ "17:15:32 | INFO | Unités trouvées : ['EUR', 'KEUR', 'MEUR', 'nan']\n",
|
|
|
+ "17:15:32 | INFO | Codes R uniques : 120\n",
|
|
|
+ "17:15:32 | INFO | Codes C uniques : 40\n",
|
|
|
+ "17:15:32 | INFO | ============================================================\n",
|
|
|
+ "17:15:32 | INFO | Terminé en 2.93s\n"
|
|
|
+ ]
|
|
|
+ }
|
|
|
+ ],
|
|
|
+ "source": [
|
|
|
+ "# =============================================================================\n",
|
|
|
+ "# ÉTAPE 5 — Export de la grande base + résumé\n",
|
|
|
+ "# =============================================================================\n",
|
|
|
+ "\n",
|
|
|
+ "def exporter(df: pd.DataFrame, chemin_sortie: Path) -> None:\n",
|
|
|
+ " \"\"\"\n",
|
|
|
+ " Sauvegarde la base consolidée en Excel et affiche un résumé.\n",
|
|
|
+ " \"\"\"\n",
|
|
|
+ " if df.empty:\n",
|
|
|
+ " log.error(\"DataFrame vide — export annulé.\")\n",
|
|
|
+ " return\n",
|
|
|
+ "\n",
|
|
|
+ " # Écriture Excel avec mise en forme légère\n",
|
|
|
+ " with pd.ExcelWriter(chemin_sortie, engine=\"openpyxl\") as writer:\n",
|
|
|
+ " df.to_excel(writer, index=False, sheet_name=\"Base_QRT\")\n",
|
|
|
+ "\n",
|
|
|
+ " # Ajustement automatique de la largeur des colonnes\n",
|
|
|
+ " ws = writer.sheets[\"Base_QRT\"]\n",
|
|
|
+ " for col in ws.columns:\n",
|
|
|
+ " max_len = max(len(str(cell.value or \"\")) for cell in col)\n",
|
|
|
+ " ws.column_dimensions[col[0].column_letter].width = max_len + 4\n",
|
|
|
+ "\n",
|
|
|
+ " # Résumé console — on force str pour éviter TypeError sur sorted()\n",
|
|
|
+ " log.info(\"=\" * 60)\n",
|
|
|
+ " log.info(\"RÉSUMÉ DE LA CONSOLIDATION\")\n",
|
|
|
+ " log.info(f\" Fichier exporté : {chemin_sortie.name}\")\n",
|
|
|
+ " log.info(f\" Lignes totales : {len(df):,}\")\n",
|
|
|
+ " log.info(f\" Entités : {sorted(df['entite'].astype(str).unique())}\")\n",
|
|
|
+ " log.info(f\" Années : {sorted(df['annee'].astype(int).unique())}\")\n",
|
|
|
+ " log.info(f\" Sections : {sorted(df['section'].astype(str).unique())}\")\n",
|
|
|
+ " log.info(f\" Unités trouvées : {sorted(df['unite'].astype(str).unique())}\")\n",
|
|
|
+ " log.info(f\" Codes R uniques : {df['R'].nunique()}\")\n",
|
|
|
+ " log.info(f\" Codes C uniques : {df['C'].nunique()}\")\n",
|
|
|
+ " log.info(\"=\" * 60)\n",
|
|
|
+ "\n",
|
|
|
+ "\n",
|
|
|
+ "# =============================================================================\n",
|
|
|
+ "# POINT D'ENTRÉE PRINCIPAL\n",
|
|
|
+ "# =============================================================================\n",
|
|
|
+ "\n",
|
|
|
+ "if __name__ == \"__main__\":\n",
|
|
|
+ " debut = datetime.now()\n",
|
|
|
+ " log.info(\"=== Démarrage consolidation QRT ===\")\n",
|
|
|
+ " log.info(f\"Dossier source : {OUTPUTS_DIR}\")\n",
|
|
|
+ "\n",
|
|
|
+ " # Étape 1 — Scan\n",
|
|
|
+ " fichiers = scanner_fichiers(OUTPUTS_DIR)\n",
|
|
|
+ "\n",
|
|
|
+ " if not fichiers:\n",
|
|
|
+ " log.error(\"Aucun fichier trouvé. Vérifiez le dossier 04 - Outputs/\")\n",
|
|
|
+ " else:\n",
|
|
|
+ " # Étapes 2, 3, 4 — Lecture + consolidation\n",
|
|
|
+ " df_final = consolider(fichiers)\n",
|
|
|
+ "\n",
|
|
|
+ " # Étape 5 — Export\n",
|
|
|
+ " exporter(df_final, BASE_FINALE)\n",
|
|
|
+ "\n",
|
|
|
+ " duree = (datetime.now() - debut).total_seconds()\n",
|
|
|
+ " log.info(f\"Terminé en {duree:.2f}s\")\n"
|
|
|
+ ]
|
|
|
+ }
|
|
|
+ ],
|
|
|
+ "metadata": {
|
|
|
+ "kernelspec": {
|
|
|
+ "display_name": "SFCR_agents",
|
|
|
+ "language": "python",
|
|
|
+ "name": "python3"
|
|
|
+ },
|
|
|
+ "language_info": {
|
|
|
+ "codemirror_mode": {
|
|
|
+ "name": "ipython",
|
|
|
+ "version": 3
|
|
|
+ },
|
|
|
+ "file_extension": ".py",
|
|
|
+ "mimetype": "text/x-python",
|
|
|
+ "name": "python",
|
|
|
+ "nbconvert_exporter": "python",
|
|
|
+ "pygments_lexer": "ipython3",
|
|
|
+ "version": "3.11.15"
|
|
|
+ }
|
|
|
+ },
|
|
|
+ "nbformat": 4,
|
|
|
+ "nbformat_minor": 5
|
|
|
+}
|