Améliorations
This commit is contained in:
parent
69272a44d6
commit
255361e9aa
@ -567,6 +567,8 @@ def build_minerai_sections(md: str) -> str:
|
|||||||
md,
|
md,
|
||||||
flags=re.DOTALL
|
flags=re.DOTALL
|
||||||
)
|
)
|
||||||
|
# suppression pour le dernier minerai dans la fiche IHH
|
||||||
|
md = re.sub(r"# Tableaux de synthèse.*<!---- AUTO-END:SECTION-IHH-TRAITEMENT -->", "", md, flags=re.DOTALL)
|
||||||
except Exception as e:
|
except Exception as e:
|
||||||
st.error(f"Erreur lors de la génération des sections IHH: {e}")
|
st.error(f"Erreur lors de la génération des sections IHH: {e}")
|
||||||
|
|
||||||
|
|||||||
@ -173,7 +173,7 @@ def interface_ia_nalyse(G_temp):
|
|||||||
|
|
||||||
if liens_chemins:
|
if liens_chemins:
|
||||||
G_final = exporter_graphe_filtre(G_temp, 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)
|
soumettre_batch(st.session_state.username, G_final)
|
||||||
st.rerun()
|
st.rerun()
|
||||||
else:
|
else:
|
||||||
@ -181,15 +181,15 @@ def interface_ia_nalyse(G_temp):
|
|||||||
|
|
||||||
elif resultat["statut"] == "terminé" and resultat["telechargement"]:
|
elif resultat["statut"] == "terminé" and resultat["telechargement"]:
|
||||||
if not st.session_state.get("telechargement_confirme"):
|
if not st.session_state.get("telechargement_confirme"):
|
||||||
st.download_button(str(_("buttons.download")), resultat["telechargement"], file_name="analyse.zip")
|
st.download_button(str(_("buttons.download")), resultat["telechargement"], file_name="analyse.zip", icon=":material/download:")
|
||||||
if st.button(str(_("pages.ia_nalyse.confirm_download"))):
|
if st.button(str(_("pages.ia_nalyse.confirm_download")), icon=":material/task_alt:"):
|
||||||
nettoyage_post_telechargement(st.session_state.username)
|
nettoyage_post_telechargement(st.session_state.username)
|
||||||
st.session_state["telechargement_confirme"] = True
|
st.session_state["telechargement_confirme"] = True
|
||||||
st.rerun()
|
st.rerun()
|
||||||
else:
|
else:
|
||||||
st.success("Résultat supprimé. Vous pouvez relancer une nouvelle analyse.")
|
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()
|
st.rerun()
|
||||||
else:
|
else:
|
||||||
if st.button(str(_("buttons.refresh"))):
|
if st.button(str(_("buttons.refresh")), icon=":material/refresh:"):
|
||||||
st.rerun()
|
st.rerun()
|
||||||
|
|||||||
@ -8,6 +8,7 @@ suivant la structure définie dans Remarques.md.
|
|||||||
import streamlit as st
|
import streamlit as st
|
||||||
import networkx as nx
|
import networkx as nx
|
||||||
import uuid
|
import uuid
|
||||||
|
import re
|
||||||
from utils.translations import _
|
from utils.translations import _
|
||||||
from utils.widgets import html_expander
|
from utils.widgets import html_expander
|
||||||
from networkx.drawing.nx_agraph import write_dot
|
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))
|
liens.add((u, v))
|
||||||
return liens
|
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):
|
def interface_plan_d_action(G_temp):
|
||||||
st.markdown(f"# {str(_('pages.plan_d_action.title'))}")
|
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)
|
G_final = exporter_graphe_filtre(G_temp, liens_chemins)
|
||||||
st.session_state["G_final"] = G_final
|
st.session_state["G_final"] = G_final
|
||||||
# formulaire ou sélection
|
# 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
|
# On déclenche la suite — mais on NE traite rien maintenant
|
||||||
st.session_state["plan_d_action"] = 1
|
st.session_state["plan_d_action"] = 1
|
||||||
st.rerun() # force la réexécution immédiatement avec état mis à jour
|
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)
|
data = extract_data_from_graph(graph, ref_graph)
|
||||||
results = calculate_vulnerabilities(data, config)
|
results = calculate_vulnerabilities(data, config)
|
||||||
report, file_names = generate_report(data, results, 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
|
st.session_state["g_md_done"] = True # pour ne pas re-traiter à chaque affichage
|
||||||
|
|
||||||
# Affichage de l’interface Streamlit
|
# Affichage de l’interface Streamlit
|
||||||
|
|||||||
@ -365,8 +365,10 @@ def afficher_bloc_ihh_isg(titre, ihh, isg, details_content=""):
|
|||||||
|
|
||||||
# 1. Afficher vulnérabilité combinée en premier
|
# 1. Afficher vulnérabilité combinée en premier
|
||||||
if "#### Vulnérabilité combinée IHH-ISG" in details_content:
|
if "#### Vulnérabilité combinée IHH-ISG" in details_content:
|
||||||
st.markdown("#### Vulnérabilité combinée IHH-ISG")
|
conteneur, = st.columns([1], gap="small", border=True)
|
||||||
afficher_section_texte(lines, "#### Vulnérabilité combinée IHH-ISG", "###")
|
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
|
# 2. Afficher ISG des pays impliqués
|
||||||
if "##### ISG des pays impliqués" in details_content:
|
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):
|
def afficher_description(titre, description):
|
||||||
st.markdown(f"## {titre}")
|
st.markdown(f"## {titre}")
|
||||||
if description:
|
conteneur, = st.columns([1], gap="small", border=True)
|
||||||
lines = description.split('\n')
|
with conteneur:
|
||||||
description_lines = []
|
if description:
|
||||||
|
lines = description.split('\n')
|
||||||
|
description_lines = []
|
||||||
|
|
||||||
# Extraire le premier paragraphe descriptif
|
# Extraire le premier paragraphe descriptif
|
||||||
for line in lines:
|
for line in lines:
|
||||||
line = line.strip()
|
line = line.strip()
|
||||||
if not line:
|
if not line:
|
||||||
if description_lines: # Si on a déjà du contenu, une ligne vide termine le paragraphe
|
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
|
break
|
||||||
continue
|
description_lines.append(line)
|
||||||
# Arrêter aux titres de sections ou tableaux
|
|
||||||
if (line.startswith('####') or
|
|
||||||
line.startswith('|') or
|
|
||||||
line.startswith('**Unité')):
|
|
||||||
break
|
|
||||||
description_lines.append(line)
|
|
||||||
|
|
||||||
if description_lines:
|
if description_lines:
|
||||||
# Rejoindre les lignes en un seul paragraphe
|
# Rejoindre les lignes en un seul paragraphe
|
||||||
full_description = ' '.join(description_lines)
|
full_description = ' '.join(description_lines)
|
||||||
st.markdown(full_description)
|
st.markdown(full_description)
|
||||||
|
else:
|
||||||
|
st.markdown("Description non disponible")
|
||||||
else:
|
else:
|
||||||
st.markdown("Description non disponible")
|
st.markdown("Description non disponible")
|
||||||
else:
|
|
||||||
st.markdown("Description non disponible")
|
|
||||||
|
|
||||||
def afficher_caracteristiques_minerai(minerai, mineraux_data, details_content=""):
|
def afficher_caracteristiques_minerai(minerai, mineraux_data, details_content=""):
|
||||||
st.markdown("### Caractéristiques générales")
|
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
|
# 3. Afficher la vulnérabilité combinée ICS-IVC en dernier
|
||||||
if "#### Vulnérabilité combinée ICS-IVC" in details_content:
|
if "#### Vulnérabilité combinée ICS-IVC" in details_content:
|
||||||
st.markdown("#### Vulnérabilité combinée ICS-IVC")
|
conteneur, = st.columns([1], gap="small", border=True)
|
||||||
afficher_section_texte(lines, "#### Vulnérabilité combinée ICS-IVC", "####")
|
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
|
# 1. Afficher la section ICS complète
|
||||||
if "#### ICS" in details_content:
|
if "#### ICS" in details_content:
|
||||||
@ -558,11 +564,12 @@ def set_vulnerability(v1, v2, t1, t2, seuils):
|
|||||||
return poids, couleur, v1_couleur, v2_couleur
|
return poids, couleur, v1_couleur, v2_couleur
|
||||||
|
|
||||||
def colorer_couleurs(la_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}]"
|
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}]"
|
return f":orange-badge[{la_couleur}]"
|
||||||
if la_couleur.lower() == "vert":
|
if t == "vert" or t == "facile":
|
||||||
return f":green-badge[{la_couleur}]"
|
return f":green-badge[{la_couleur}]"
|
||||||
return 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
|
* 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"):
|
with st.expander("Préconisations et indicateurs génériques"):
|
||||||
col_left, col_right = st.columns([1, 1], gap="small", border=True)
|
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")
|
st.markdown("Mise en œuvre : \n")
|
||||||
for niveau, contenu in PRECONISATIONS.items():
|
for niveau, contenu in PRECONISATIONS.items():
|
||||||
if niveau in niveau_criticite:
|
if niveau in niveau_criticite:
|
||||||
contenu_md = f"* {niveau}\n"
|
contenu_md = f"* {colorer_couleurs(niveau)}\n"
|
||||||
for p in PRECONISATIONS[niveau]:
|
for p in PRECONISATIONS[niveau]:
|
||||||
contenu_md += f" - {p}\n"
|
contenu_md += f" - {p}\n"
|
||||||
st.markdown(contenu_md)
|
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")
|
st.markdown("Mise en œuvre : \n")
|
||||||
for niveau, contenu in INDICATEURS.items():
|
for niveau, contenu in INDICATEURS.items():
|
||||||
if niveau in niveau_criticite:
|
if niveau in niveau_criticite:
|
||||||
contenu_md = f"* {niveau}\n"
|
contenu_md = f"* {colorer_couleurs(niveau)}\n"
|
||||||
for p in INDICATEURS[niveau]:
|
for p in INDICATEURS[niveau]:
|
||||||
contenu_md += f" - {p}\n"
|
contenu_md += f" - {p}\n"
|
||||||
st.markdown(contenu_md)
|
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)
|
niveau_criticite_operation["Extraction"] = affectation_poids(poids_E)
|
||||||
|
|
||||||
for operation in ["Assemblage", "Fabrication", "Traitement", "Extraction"]:
|
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}"):
|
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)
|
col_left, col_right = st.columns([1, 1], gap="small", border=True)
|
||||||
with col_left:
|
with col_left:
|
||||||
st.markdown("#### Préconisations :\n\n")
|
st.markdown("#### Préconisations :\n\n")
|
||||||
st.markdown("Mise en œuvre : \n")
|
st.markdown("Mise en œuvre : \n")
|
||||||
for niveau, contenu in PRECONISATIONS[operation].items():
|
for niveau, contenu in PRECONISATIONS[operation].items():
|
||||||
if niveau in niveau_criticite_operation[operation]:
|
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]:
|
for p in PRECONISATIONS[operation][niveau]:
|
||||||
contenu_md += f" - {p}\n"
|
contenu_md += f" - {p}\n"
|
||||||
st.markdown(contenu_md)
|
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")
|
st.markdown("Mise en œuvre : \n")
|
||||||
for niveau, contenu in INDICATEURS[operation].items():
|
for niveau, contenu in INDICATEURS[operation].items():
|
||||||
if niveau in niveau_criticite_operation[operation]:
|
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]:
|
for p in INDICATEURS[operation][niveau]:
|
||||||
contenu_md += f" - {p}\n"
|
contenu_md += f" - {p}\n"
|
||||||
st.markdown(contenu_md)
|
st.markdown(contenu_md)
|
||||||
|
|
||||||
st.markdown("---")
|
st.markdown("## Détails des opérations")
|
||||||
|
|
||||||
with st.expander(f"{sel_prod} et Assemblage"):
|
with st.expander(f"{sel_prod} et Assemblage"):
|
||||||
assemblage_details = details_sections.get(f"{sel_prod}_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", "")
|
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)
|
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_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)
|
afficher_caracteristiques_minerai(sel_miner, mineraux[sel_miner], minerai_general)
|
||||||
|
|
||||||
st.markdown("---")
|
|
||||||
|
|||||||
17
schema.txt
17
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];
|
HP_Chine_Assemblage_OrdiBureau [fillcolor="#d1e0ff", label="HP China", niveau=12];
|
||||||
Lenovo_Chine_Assemblage_OrdiBureau [fillcolor="#d1e0ff", label=Lenovo, niveau=12];
|
Lenovo_Chine_Assemblage_OrdiBureau [fillcolor="#d1e0ff", label=Lenovo, niveau=12];
|
||||||
OrdiPortable [fillcolor="#a0d6ff", label="Ordinateur portable", niveau=0];
|
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];
|
Assemblage_OrdiPortable [fillcolor="#ffd699", ihh_acteurs=14, ihh_pays=35, label=Assemblage, niveau=10];
|
||||||
Bresil_Assemblage_OrdiPortable [fillcolor="#e6f2ff", label=Brésil, niveau=11];
|
Bresil_Assemblage_OrdiPortable [fillcolor="#e6f2ff", label=Brésil, niveau=11];
|
||||||
Chine_Assemblage_OrdiPortable [fillcolor="#e6f2ff", label=Chine, niveau=11];
|
Chine_Assemblage_OrdiPortable [fillcolor="#e6f2ff", label=Chine, niveau=11];
|
||||||
@ -2476,6 +2477,22 @@ OrdiPortable -> SSDM2;
|
|||||||
OrdiPortable -> ProcesseurX86;
|
OrdiPortable -> ProcesseurX86;
|
||||||
OrdiPortable -> SSD25;
|
OrdiPortable -> SSD25;
|
||||||
OrdiPortable -> Assemblage_OrdiPortable;
|
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 -> 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 -> Chine_Assemblage_OrdiPortable [color=purple, fontcolor=purple, label="56%", poids=2];
|
||||||
Assemblage_OrdiPortable -> Vietnam_Assemblage_OrdiPortable [color=purple, fontcolor=purple, label="11%", poids=1];
|
Assemblage_OrdiPortable -> Vietnam_Assemblage_OrdiPortable [color=purple, fontcolor=purple, label="11%", poids=1];
|
||||||
|
|||||||
Loading…
x
Reference in New Issue
Block a user