2025-06-11 14:57:53 +02:00

106 lines
3.6 KiB
Python

# ics.py
import re
import yaml
import pandas as pd
import unicodedata
import textwrap
PAIR_RE = re.compile(r"```yaml[^\n]*\n(.*?)```", re.S | re.I)
def _normalize_unicode(text: str) -> str:
return unicodedata.normalize("NFKC", text)
def _pairs_dataframe(md: str) -> pd.DataFrame:
rows = []
for raw in PAIR_RE.findall(md):
bloc = yaml.safe_load(raw)
if isinstance(bloc, dict) and "pair" in bloc:
rows.append(bloc["pair"])
return pd.DataFrame(rows)
def _fill(segment: str, pair: dict) -> str:
segment = _normalize_unicode(segment)
for k, v in pair.items():
val = f"{v:.2f}" if isinstance(v, (int, float)) else str(v)
segment = re.sub(
rf"{{{{\s*{re.escape(k)}\s*}}}}",
val,
segment,
flags=re.I,
)
segment = re.sub(
r"ICS\s*=\s*[-+]?\d+(?:\.\d+)?",
f"ICS = {pair['ics']:.2f}",
segment,
count=1,
)
return segment
def _segments(md: str):
blocs = list(PAIR_RE.finditer(md))
for i, match in enumerate(blocs):
pair = yaml.safe_load(match.group(1))["pair"]
start = match.end()
end = blocs[i + 1].start() if i + 1 < len(blocs) else len(md)
segment = md[start:end]
yield pair, segment
def _pivot(df: pd.DataFrame) -> str:
out = []
for min_, g in df.groupby("minerai"):
out += [f"## {min_}",
"| Composant | ICS | Faisabilité technique | Délai d'implémentation | Impact économique |",
"| :-- | :--: | :--: | :--: | :--: |"]
for _, r in g.sort_values("ics", ascending=False).iterrows():
out += [f"| {r.composant} | {r.ics:.2f} | {r.f_tech:.2f} | "
f"{r.delai:.2f} | {r.cout:.2f} |"]
out.append("")
return "\n".join(out)
def _synth(df: pd.DataFrame) -> str:
lignes = ["| Composant | Minerai | ICS |", "| :-- | :-- | :--: |"]
for _, r in df.sort_values("ics", ascending=False).iterrows():
lignes.append(f"| {r.composant} | {r.minerai} | {r.ics:.2f} |")
return "\n".join(lignes)
def build_dynamic_sections(md_raw: str) -> str:
"""
Procédure pour construire et remplacer les sections dynamiques dans les fiches d'analyse produit (ICS).
Cette fonction permet de :
1. Extraire les données structurées en YAML des blocs du markdown.
2. Générer un tableau pivotant les données sur la criticité et faisabilité technique.
3. Produire une synthèse finale avec l'analyse critique par composant.
Args:
md (str): Contenu brut du fichier Markdown contenant les structures YAML à analyser.
Returns:
str: Le markdown enrichi des tableaux de donnée analysés, ou le contenu original inchangé si aucun bloc structuré n'est trouvé.
"""
md_raw = _normalize_unicode(md_raw)
df = _pairs_dataframe(md_raw)
if df.empty:
return md_raw
couples = ["# Criticité par couple Composant -> Minerai"]
for pair, seg in _segments(md_raw):
if pair:
couples.append(_fill(seg, pair))
couples_md = "\n".join(couples)
pivot_md = _pivot(df)
synth_md = _synth(df)
md = re.sub(r"#\s+Criticité par couple.*", couples_md, md_raw, flags=re.S | re.I)
md = re.sub(r"<!---- AUTO-BEGIN:PIVOT -->.*?<!---- AUTO-END:PIVOT -->",
f"<!---- AUTO-BEGIN:PIVOT -->\n{pivot_md}\n<!---- AUTO-END:PIVOT -->",
md, flags=re.S)
md = re.sub(r"<!---- AUTO-BEGIN:TABLEAU-FINAL -->.*?<!---- AUTO-END:TABLEAU-FINAL -->",
f"<!---- AUTO-BEGIN:TABLEAU-FINAL -->\n{synth_md}\n<!---- AUTO-END:TABLEAU-FINAL -->",
md, flags=re.S)
return textwrap.dedent(md)