Stéphan Peccini 6d2e877341
feat(audit): audit qualité complet — 907→0 erreurs ruff + fix multiselect labels
- Correction des 907 erreurs ruff (pathlib, imports, nommage, simplifications, docstrings)
- Fix déduplication labels dans multiselect nœuds d'arrivée (analyse)
- Expansion 1→N label→IDs pour le Sankey (Pays d'opération)
- Ajout CLAUDE.md et document de design de l'audit
- Mise à jour .gitignore (artefacts tests exploratoires)

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
2026-03-02 11:52:01 +01:00

157 lines
6.3 KiB
Python

# production.py
# Ce module gère à la fois les fiches d'assemblage ET de fabrication.
import re
from pathlib import Path
import streamlit as st
import yaml
from config import FICHES_CRITICITE
def build_production_sections(md: str) -> str:
"""Procédure pour construire et remplacer les sections des fiches de production.
Cette fonction permet d'extraire les données du markdown, organiser
les informations sur les pays d'implantation et acteurs, puis générer
un tableau structuré dans l'intervention. Le code prend en charge
deux types de fiches : fabrication et assemblage.
Args:
md (str): Fichier Markdown à traiter contenant la structure YAML des sections.
Returns:
str: Markdown modifié avec les tableaux construits selon le type de fiche.
"""
schema = None
type_fiche = None
front_match = re.match(r"(?s)^---\n(.*?)\n---\n", md)
if front_match:
try:
front_matter = yaml.safe_load(front_match.group(1))
schema = front_matter.get("schema")
type_fiche = front_matter.get("type_fiche")
if type_fiche not in ["assemblage", "fabrication"] or not schema:
return md
except Exception as e:
st.error(f"Erreur lors du chargement du front matter: {e}")
return md
yaml_block = re.search(r"```yaml\n(.+?)\n```", md, re.DOTALL)
if not yaml_block:
return md
# Capture le bloc YAML complet pour le supprimer plus tard
yaml_block_full = yaml_block.group(0)
try:
yaml_data = yaml.safe_load(yaml_block.group(1))
except Exception as e:
st.error(f"Erreur lors du chargement du YAML: {e}")
return md
if not isinstance(yaml_data, dict) or len(yaml_data) == 0:
return md
produit_key = list(yaml_data.keys())[0]
produit_data = yaml_data[produit_key]
pays_data = []
for _pays_key, pays_info in produit_data.items():
nom_pays = pays_info.get('nom_du_pays', '')
part_marche_pays = pays_info.get('part_de_marche', '0%')
part_marche_num = float(part_marche_pays.strip('%'))
acteurs = []
for _acteur_key, acteur_info in pays_info.get('acteurs', {}).items():
nom_acteur = acteur_info.get('nom_de_l_acteur', '')
part_marche_acteur = acteur_info.get('part_de_marche', '0%')
pays_origine = acteur_info.get('pays_d_origine', '')
part_marche_acteur_num = float(part_marche_acteur.strip('%'))
acteurs.append({
'nom': nom_acteur,
'part_marche': part_marche_acteur,
'part_marche_num': part_marche_acteur_num,
'pays_origine': pays_origine
})
acteurs_tries = sorted(acteurs, key=lambda x: x['part_marche_num'], reverse=True)
pays_data.append({
'nom': nom_pays,
'part_marche': part_marche_pays,
'part_marche_num': part_marche_num,
'acteurs': acteurs_tries
})
pays_tries = sorted(pays_data, key=lambda x: x['part_marche_num'], reverse=True)
lignes_tableau = [
"| **Pays d'implantation** | **Entreprise** | **Pays d'origine** | **Part de marché** |",
"| :-- | :-- | :-- | :-- |"
]
for pays in pays_tries:
for acteur in pays['acteurs']:
part_marche_formattee = acteur['part_marche'].strip('%') + ' %'
lignes_tableau.append(
f"| {pays['nom']} | {acteur['nom']} | {acteur['pays_origine']} | {part_marche_formattee} |"
)
part_marche_pays_formattee = pays['part_marche'].strip('%') + ' %'
lignes_tableau.append(
f"| **{pays['nom']}** | **Total** | **{pays['nom']}** | **{part_marche_pays_formattee}** |"
)
tableau_final = "\n".join(lignes_tableau)
if type_fiche == "fabrication":
md_modifie = re.sub(
r"<!---- AUTO-BEGIN:TABLEAU-FABRICANTS -->.*?<!---- AUTO-END:TABLEAU-FABRICANTS -->",
f"<!---- AUTO-BEGIN:TABLEAU-FABRICANTS -->\n{tableau_final}\n<!---- AUTO-END:TABLEAU-FABRICANTS -->",
md,
flags=re.DOTALL
)
else:
md_modifie = re.sub(
r"<!---- AUTO-BEGIN:TABLEAU-ASSEMBLEURS -->.*?<!---- AUTO-END:TABLEAU-ASSEMBLEURS -->",
f"<!---- AUTO-BEGIN:TABLEAU-ASSEMBLEURS -->\n{tableau_final}\n<!---- AUTO-END:TABLEAU-ASSEMBLEURS -->",
md,
flags=re.DOTALL
)
# Chercher et remplacer la section IHH si un schéma a été identifié
if schema:
# Charger le contenu de la fiche technique IHH
try:
# Essayer de lire le fichier depuis le système de fichiers
with Path(FICHES_CRITICITE["IHH"]).open(encoding="utf-8") as f:
ihh_content = f.read()
# Chercher la section IHH correspondant au schéma et au type de fiche
# Format de la section : ## Assemblage/Fabrication - [Schema]
if type_fiche == "fabrication":
ihh_section_pattern = rf"## Fabrication - {schema}\s*\n### Indice de Herfindahl-Hirschmann[\s\S]*?(?=\n## |$)"
else: # type_fiche == "assemblage"
ihh_section_pattern = rf"## Assemblage - {schema}\s*\n### Indice de Herfindahl-Hirschmann[\s\S]*?(?=\n## |$)"
ihh_section_match = re.search(ihh_section_pattern, ihh_content)
if ihh_section_match:
# Extraire la section complète sans le titre principal
ihh_section = ihh_section_match.group(0).split("\n", 2)[2].strip()
# Remplacer la section IHH dans la fiche d'assemblage
md_modifie = re.sub(
r"<!---- AUTO-BEGIN:SECTION-IHH -->.*?<!---- AUTO-END:SECTION-IHH -->",
f"<!---- AUTO-BEGIN:SECTION-IHH -->\n{ihh_section}\n<!---- AUTO-END:SECTION-IHH -->",
md_modifie,
flags=re.DOTALL
)
else:
# Si aucune section IHH n'est trouvée pour ce schéma, laisser la section existante
st.warning(f"Aucune section IHH trouvée pour le schéma {schema} dans la fiche technique IHH.")
except Exception as e:
st.error(f"Erreur lors de la lecture/traitement de la fiche IHH: {e}")
# Supprimer le bloc YAML du markdown final
return md_modifie.replace(yaml_block_full, "")