diff --git a/app/fiches/utils/dynamic/minerai/minerai.py b/app/fiches/utils/dynamic/minerai/minerai.py index 9710646..960e2c2 100644 --- a/app/fiches/utils/dynamic/minerai/minerai.py +++ b/app/fiches/utils/dynamic/minerai/minerai.py @@ -567,6 +567,8 @@ def build_minerai_sections(md: str) -> str: md, flags=re.DOTALL ) + # suppression pour le dernier minerai dans la fiche IHH + md = re.sub(r"# Tableaux de synthèse.*", "", md, flags=re.DOTALL) except Exception as e: st.error(f"Erreur lors de la génération des sections IHH: {e}") diff --git a/app/ia_nalyse/interface.py b/app/ia_nalyse/interface.py index 54c7f09..6cc2ec7 100644 --- a/app/ia_nalyse/interface.py +++ b/app/ia_nalyse/interface.py @@ -173,7 +173,7 @@ def interface_ia_nalyse(G_temp): if liens_chemins: G_final = exporter_graphe_filtre(G_temp, liens_chemins) - if st.button(str(_("pages.ia_nalyse.submit_request"))): + if st.button(str(_("pages.ia_nalyse.submit_request")), icon=":material/send:"): soumettre_batch(st.session_state.username, G_final) st.rerun() else: @@ -181,15 +181,15 @@ def interface_ia_nalyse(G_temp): elif resultat["statut"] == "terminé" and resultat["telechargement"]: if not st.session_state.get("telechargement_confirme"): - st.download_button(str(_("buttons.download")), resultat["telechargement"], file_name="analyse.zip") - if st.button(str(_("pages.ia_nalyse.confirm_download"))): + st.download_button(str(_("buttons.download")), resultat["telechargement"], file_name="analyse.zip", icon=":material/download:") + if st.button(str(_("pages.ia_nalyse.confirm_download")), icon=":material/task_alt:"): nettoyage_post_telechargement(st.session_state.username) st.session_state["telechargement_confirme"] = True st.rerun() else: st.success("Résultat supprimé. Vous pouvez relancer une nouvelle analyse.") - if st.button(str(_("buttons.refresh"))): + if st.button(str(_("buttons.refresh")), icon=":material/refresh:"): st.rerun() else: - if st.button(str(_("buttons.refresh"))): + if st.button(str(_("buttons.refresh")), icon=":material/refresh:"): st.rerun() diff --git a/app/plan_d_action/interface.py b/app/plan_d_action/interface.py index a30e000..602f3f7 100644 --- a/app/plan_d_action/interface.py +++ b/app/plan_d_action/interface.py @@ -8,6 +8,7 @@ suivant la structure définie dans Remarques.md. import streamlit as st import networkx as nx import uuid +import re from utils.translations import _ from utils.widgets import html_expander from networkx.drawing.nx_agraph import write_dot @@ -169,6 +170,24 @@ def extraire_liens_filtres(chemins, niveaux, niveau_depart, niveau_arrivee, nive liens.add((u, v)) return liens +CORRESPONDANCE_COULEURS = { + "Rouge": "red", + "Orange": "orange", + "Vert": "green", + "FAIBLE": "green", + "MODÉRÉE": "orange", + "ÉLEVÉE à CRITIQUE": "red" +} + +def remplacer_par_badge(markdown_text, correspondance=CORRESPONDANCE_COULEURS): + # Échappe les mots à remplacer s'ils contiennent des accents ou espaces + for mot, couleur in correspondance.items(): + # Utilise des bords de mots (\b) pour éviter les remplacements partiels + pattern = r'\b' + re.escape(mot) + r'\b' + remplacement = f":{couleur}-badge[{mot}]" + markdown_text = re.sub(pattern, remplacement, markdown_text) + return markdown_text + def interface_plan_d_action(G_temp): st.markdown(f"# {str(_('pages.plan_d_action.title'))}") @@ -208,7 +227,7 @@ def interface_plan_d_action(G_temp): G_final = exporter_graphe_filtre(G_temp, liens_chemins) st.session_state["G_final"] = G_final # formulaire ou sélection - if st.button(str(_("pages.plan_d_action.submit_request")), icon=":material/play_arrow:"): + if st.button(str(_("pages.plan_d_action.submit_request")), icon=":material/send:"): # On déclenche la suite — mais on NE traite rien maintenant st.session_state["plan_d_action"] = 1 st.rerun() # force la réexécution immédiatement avec état mis à jour @@ -223,7 +242,7 @@ def interface_plan_d_action(G_temp): data = extract_data_from_graph(graph, ref_graph) results = calculate_vulnerabilities(data, config) report, file_names = generate_report(data, results, config) - write_report(report, st.session_state["G_md"]) + write_report(remplacer_par_badge(report), st.session_state["G_md"]) st.session_state["g_md_done"] = True # pour ne pas re-traiter à chaque affichage # Affichage de l’interface Streamlit diff --git a/app/plan_d_action/plan_d_action.py b/app/plan_d_action/plan_d_action.py index b319c5d..0aa17e1 100644 --- a/app/plan_d_action/plan_d_action.py +++ b/app/plan_d_action/plan_d_action.py @@ -365,8 +365,10 @@ def afficher_bloc_ihh_isg(titre, ihh, isg, details_content=""): # 1. Afficher vulnérabilité combinée en premier if "#### Vulnérabilité combinée IHH-ISG" in details_content: - st.markdown("#### Vulnérabilité combinée IHH-ISG") - afficher_section_texte(lines, "#### Vulnérabilité combinée IHH-ISG", "###") + conteneur, = st.columns([1], gap="small", border=True) + with conteneur: + st.markdown("#### Vulnérabilité combinée IHH-ISG") + afficher_section_texte(lines, "#### Vulnérabilité combinée IHH-ISG", "###") # 2. Afficher ISG des pays impliqués if "##### ISG des pays impliqués" in details_content: @@ -442,32 +444,34 @@ def afficher_section_texte(lines, section_start, section_end_marker=None): def afficher_description(titre, description): st.markdown(f"## {titre}") - if description: - lines = description.split('\n') - description_lines = [] + conteneur, = st.columns([1], gap="small", border=True) + with conteneur: + if description: + lines = description.split('\n') + description_lines = [] - # Extraire le premier paragraphe descriptif - for line in lines: - line = line.strip() - if not line: - if description_lines: # Si on a déjà du contenu, une ligne vide termine le paragraphe + # Extraire le premier paragraphe descriptif + for line in lines: + line = line.strip() + if not line: + if description_lines: # Si on a déjà du contenu, une ligne vide termine le paragraphe + break + continue + # Arrêter aux titres de sections ou tableaux + if (line.startswith('####') or + line.startswith('|') or + line.startswith('**Unité')): break - continue - # Arrêter aux titres de sections ou tableaux - if (line.startswith('####') or - line.startswith('|') or - line.startswith('**Unité')): - break - description_lines.append(line) + description_lines.append(line) - if description_lines: - # Rejoindre les lignes en un seul paragraphe - full_description = ' '.join(description_lines) - st.markdown(full_description) + if description_lines: + # Rejoindre les lignes en un seul paragraphe + full_description = ' '.join(description_lines) + st.markdown(full_description) + else: + st.markdown("Description non disponible") else: st.markdown("Description non disponible") - else: - st.markdown("Description non disponible") def afficher_caracteristiques_minerai(minerai, mineraux_data, details_content=""): st.markdown("### Caractéristiques générales") @@ -480,8 +484,10 @@ def afficher_caracteristiques_minerai(minerai, mineraux_data, details_content="" # 3. Afficher la vulnérabilité combinée ICS-IVC en dernier if "#### Vulnérabilité combinée ICS-IVC" in details_content: - st.markdown("#### Vulnérabilité combinée ICS-IVC") - afficher_section_texte(lines, "#### Vulnérabilité combinée ICS-IVC", "####") + conteneur, = st.columns([1], gap="small", border=True) + with conteneur: + st.markdown("#### Vulnérabilité combinée ICS-IVC") + afficher_section_texte(lines, "#### Vulnérabilité combinée ICS-IVC", "####") # 1. Afficher la section ICS complète if "#### ICS" in details_content: @@ -558,11 +564,12 @@ def set_vulnerability(v1, v2, t1, t2, seuils): return poids, couleur, v1_couleur, v2_couleur def colorer_couleurs(la_couleur): - if la_couleur.lower() == "rouge": + t = la_couleur.lower() + if t == "rouge" or t == "difficile": return f":red-badge[{la_couleur}]" - if la_couleur.lower() == "orange": + if t == "orange" or t == "modérée": return f":orange-badge[{la_couleur}]" - if la_couleur.lower() == "vert": + if t == "vert" or t == "facile": return f":green-badge[{la_couleur}]" return la_couleur @@ -721,7 +728,7 @@ def initialiser_interface(filepath: str, config_path: str = "assets/config.yaml" * pondération de la Substitution dans le calcul de la criticité globale : 2 """) - st.markdown("---") + st.markdown("## Préconisations et indicateurs") with st.expander("Préconisations et indicateurs génériques"): col_left, col_right = st.columns([1, 1], gap="small", border=True) @@ -730,7 +737,7 @@ def initialiser_interface(filepath: str, config_path: str = "assets/config.yaml" st.markdown("Mise en œuvre : \n") for niveau, contenu in PRECONISATIONS.items(): if niveau in niveau_criticite: - contenu_md = f"* {niveau}\n" + contenu_md = f"* {colorer_couleurs(niveau)}\n" for p in PRECONISATIONS[niveau]: contenu_md += f" - {p}\n" st.markdown(contenu_md) @@ -739,7 +746,7 @@ def initialiser_interface(filepath: str, config_path: str = "assets/config.yaml" st.markdown("Mise en œuvre : \n") for niveau, contenu in INDICATEURS.items(): if niveau in niveau_criticite: - contenu_md = f"* {niveau}\n" + contenu_md = f"* {colorer_couleurs(niveau)}\n" for p in INDICATEURS[niveau]: contenu_md += f" - {p}\n" st.markdown(contenu_md) @@ -760,15 +767,21 @@ def initialiser_interface(filepath: str, config_path: str = "assets/config.yaml" niveau_criticite_operation["Extraction"] = affectation_poids(poids_E) for operation in ["Assemblage", "Fabrication", "Traitement", "Extraction"]: + if operation == "Assemblage": + item = sel_prod + elif operation == "Fabrication": + item = sel_comp + else: + item = sel_miner with st.expander(f"Préconisations et indicateurs spécifiques - {operation}"): - st.markdown(f"### {operation}") + st.markdown(f"### {operation} -> :blue-background[{item}]") col_left, col_right = st.columns([1, 1], gap="small", border=True) with col_left: st.markdown("#### Préconisations :\n\n") st.markdown("Mise en œuvre : \n") for niveau, contenu in PRECONISATIONS[operation].items(): if niveau in niveau_criticite_operation[operation]: - contenu_md = f"* {niveau}\n" + contenu_md = f"* {colorer_couleurs(niveau)}\n" for p in PRECONISATIONS[operation][niveau]: contenu_md += f" - {p}\n" st.markdown(contenu_md) @@ -777,12 +790,12 @@ def initialiser_interface(filepath: str, config_path: str = "assets/config.yaml" st.markdown("Mise en œuvre : \n") for niveau, contenu in INDICATEURS[operation].items(): if niveau in niveau_criticite_operation[operation]: - contenu_md = f"* {niveau}\n" + contenu_md = f"* {colorer_couleurs(niveau)}\n" for p in INDICATEURS[operation][niveau]: contenu_md += f" - {p}\n" st.markdown(contenu_md) - st.markdown("---") + st.markdown("## Détails des opérations") with st.expander(f"{sel_prod} et Assemblage"): assemblage_details = details_sections.get(f"{sel_prod}_assemblage", "") @@ -802,9 +815,7 @@ def initialiser_interface(filepath: str, config_path: str = "assets/config.yaml" extraction_details = details_sections.get(f"{sel_miner}_extraction", "") afficher_bloc_ihh_isg("Extraction", mineraux[sel_miner]["IHH_Extraction"], mineraux[sel_miner]["ISG_Extraction"], extraction_details) - traitement_details = details_sections.get(f"{sel_miner}_traitement", "") + traitement_details = details_sections.get(f"{sel_miner}_traitement", "").removesuffix("\n---\n") afficher_bloc_ihh_isg("Traitement", mineraux[sel_miner]["IHH_Traitement"], mineraux[sel_miner]["ISG_Traitement"], traitement_details) afficher_caracteristiques_minerai(sel_miner, mineraux[sel_miner], minerai_general) - - st.markdown("---") diff --git a/schema.txt b/schema.txt index 1554299..7c580b6 100644 --- a/schema.txt +++ b/schema.txt @@ -137,6 +137,7 @@ ASUS_Taiwan_Assemblage_OrdiBureau [fillcolor="#d1e0ff", label=ASUS, niveau=12]; HP_Chine_Assemblage_OrdiBureau [fillcolor="#d1e0ff", label="HP China", niveau=12]; Lenovo_Chine_Assemblage_OrdiBureau [fillcolor="#d1e0ff", label=Lenovo, niveau=12]; OrdiPortable [fillcolor="#a0d6ff", label="Ordinateur portable", niveau=0]; +StationTravailPortable [fillcolor="#a0d6ff", label="Station de travail portable", niveau=0]; Assemblage_OrdiPortable [fillcolor="#ffd699", ihh_acteurs=14, ihh_pays=35, label=Assemblage, niveau=10]; Bresil_Assemblage_OrdiPortable [fillcolor="#e6f2ff", label=Brésil, niveau=11]; Chine_Assemblage_OrdiPortable [fillcolor="#e6f2ff", label=Chine, niveau=11]; @@ -2476,6 +2477,22 @@ OrdiPortable -> SSDM2; OrdiPortable -> ProcesseurX86; OrdiPortable -> SSD25; OrdiPortable -> Assemblage_OrdiPortable; + +StationTravailPortable -> Audio; +StationTravailPortable -> Camera; +StationTravailPortable -> Capteurs; +StationTravailPortable -> Connecteurs; +StationTravailPortable -> EcranOLED; +StationTravailPortable -> Boitier; +StationTravailPortable -> Connectivite; +StationTravailPortable -> Batterie; +StationTravailPortable -> MemoireRAM; +StationTravailPortable -> CarteMere; +StationTravailPortable -> ProcesseurARM; +StationTravailPortable -> SSD25; +StationTravailPortable -> Television; +StationTravailPortable -> Assemblage_OrdiPortable; + Assemblage_OrdiPortable -> Bresil_Assemblage_OrdiPortable [color=purple, fontcolor=purple, label="3%", poids=1]; Assemblage_OrdiPortable -> Chine_Assemblage_OrdiPortable [color=purple, fontcolor=purple, label="56%", poids=2]; Assemblage_OrdiPortable -> Vietnam_Assemblage_OrdiPortable [color=purple, fontcolor=purple, label="11%", poids=1];